Use boost::unordered_node_map for CCoinsMap

boost::unordered_node_map is a highly optimized hashmap, available since
boost 1.82, that is API compatible to std::unordered_map for our use case.
It also can use the existing PoolAllocator.
This commit is contained in:
Martin Leitner-Ankerl 2025-03-23 11:27:25 +01:00
parent 723c49b63b
commit 18b2c263aa
6 changed files with 34 additions and 9 deletions

View file

@ -26,7 +26,7 @@ function(add_boost_if_needed)
cmake_policy(SET CMP0167 OLD)
endif()
set(Boost_NO_BOOST_CMAKE ON)
find_package(Boost 1.73.0 REQUIRED)
find_package(Boost 1.82.0 REQUIRED)
mark_as_advanced(Boost_INCLUDE_DIR)
set_target_properties(Boost::headers PROPERTIES IMPORTED_GLOBAL TRUE)
target_compile_definitions(Boost::headers INTERFACE

View file

@ -18,7 +18,7 @@ Bitcoin Core requires one of the following compilers.
| Dependency | Releases | Version used | Minimum required | Runtime |
| --- | --- | --- | --- | --- |
| CMake | [link](https://cmake.org/) | N/A | [3.22](https://github.com/bitcoin/bitcoin/pull/30454) | No |
| [Boost](../depends/packages/boost.mk) | [link](https://www.boost.org/users/download/) | [1.81.0](https://github.com/bitcoin/bitcoin/pull/26557) | [1.73.0](https://github.com/bitcoin/bitcoin/pull/29066) | No |
| [Boost](../depends/packages/boost.mk) | [link](https://www.boost.org/users/download/) | [1.82.0](https://github.com/bitcoin/bitcoin/pull/26557) | [1.82.0](https://github.com/bitcoin/bitcoin/pull/29066) | No |
| [libevent](../depends/packages/libevent.mk) | [link](https://github.com/libevent/libevent/releases) | [2.1.12-stable](https://github.com/bitcoin/bitcoin/pull/21991) | [2.1.8](https://github.com/bitcoin/bitcoin/pull/24681) | No |
| glibc | [link](https://www.gnu.org/software/libc/) | N/A | [2.31](https://github.com/bitcoin/bitcoin/pull/29987) | Yes |
| Linux Kernel (if building that platform) | [link](https://www.kernel.org/) | N/A | [3.17.0](https://github.com/bitcoin/bitcoin/pull/27699) | Yes |

View file

@ -15,6 +15,7 @@
#include <uint256.h>
#include <util/check.h>
#include <util/hasher.h>
#include <boost/unordered/unordered_node_map.hpp>
#include <assert.h>
#include <stdint.h>
@ -221,12 +222,12 @@ public:
* Using an additional sizeof(void*)*4 for MAX_BLOCK_SIZE_BYTES should thus be sufficient so that
* all implementations can allocate the nodes from the PoolAllocator.
*/
using CCoinsMap = std::unordered_map<COutPoint,
CCoinsCacheEntry,
SaltedOutpointHasher,
std::equal_to<COutPoint>,
PoolAllocator<CoinsCachePair,
sizeof(CoinsCachePair) + sizeof(void*) * 4>>;
using CCoinsMap = boost::unordered_node_map<COutPoint,
CCoinsCacheEntry,
SaltedOutpointHasher,
std::equal_to<COutPoint>,
PoolAllocator<CoinsCachePair,
sizeof(CoinsCachePair) + sizeof(void*) * 4>>;
using CCoinsMapMemoryResource = CCoinsMap::allocator_type::ResourceType;

View file

@ -19,6 +19,7 @@
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <boost/unordered/unordered_node_map.hpp>
namespace memusage
@ -215,6 +216,25 @@ static inline size_t DynamicUsage(const std::unordered_map<Key,
return usage_resource + usage_chunks + MallocUsage(sizeof(void*) * m.bucket_count());
}
template <class Key, class T, class Hash, class Pred, std::size_t MAX_BLOCK_SIZE_BYTES, std::size_t ALIGN_BYTES>
static inline size_t DynamicUsage(const boost::unordered_node_map<Key,
T,
Hash,
Pred,
PoolAllocator<std::pair<const Key, T>,
MAX_BLOCK_SIZE_BYTES,
ALIGN_BYTES>>& m)
{
auto* pool_resource = m.get_allocator().resource();
// The allocated chunks are stored in a std::list. Size per node should
// therefore be 3 pointers: next, previous, and a pointer to the chunk.
size_t estimated_list_node_size = MallocUsage(sizeof(void*) * 3);
size_t usage_resource = estimated_list_node_size * pool_resource->NumAllocatedChunks();
size_t usage_chunks = MallocUsage(pool_resource->ChunkSizeBytes()) * pool_resource->NumAllocatedChunks();
return usage_resource + usage_chunks + MallocUsage(sizeof(void*) * m.bucket_count());
}
} // namespace memusage
#endif // BITCOIN_MEMUSAGE_H

View file

@ -158,7 +158,7 @@ BOOST_AUTO_TEST_CASE(memusage_test)
{
auto std_map = std::unordered_map<int64_t, int64_t>{};
using Map = std::unordered_map<int64_t,
using Map = boost::unordered_node_map<int64_t,
int64_t,
std::hash<int64_t>,
std::equal_to<int64_t>,

View file

@ -35,6 +35,10 @@ private:
const uint64_t k0, k1;
public:
// instructs Boost.Unordered to not use post-mixing. We can do this because the hash is of high quality.
// This should have a slight performance benefit.
using is_avalanching = std::true_type;
SaltedOutpointHasher(bool deterministic = false);
/**