Use a pool allocator for shared_ptrs with blocks, elections and votes #2047
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
After profiling heap usage with a high amount of real-time transactions I found a couple places that could benefit from using a memory pool. In #2025 one had been added for block deserialization, however because the block objects were put in a
std::shared_ptr
after a heap allocation is still done for the control block. After profiling CPU usage it was found there wasn't much improvement (although less heap memory is now allocated). So I'm now usingstd::allocate_shared
which is similar tostd::make_shared
except you can pass a custom allocator where the memory for both the object and control block can be taken from a pool instead of from the defaultnew
. This made the purging of memory at the end a bit more difficult because underneath it uses aboost::singleton_pool
which is based on the size of the allocations, which is hidden behind details for how thestd::shared_ptr
is implemented. Luckily most platforms are quite similar in how they implementstd::shared_ptr
, Mac/Clang uses 8 more bytes which is checked for. It's not the end of the world if the pools are not purged at the end of main, it will only affect memory analysers.During node bootstrapping there can be a large number of blocks existing in the block processor (300MB worth on my run). This will never be reclaimed when using a memory pool until the node is stopped. Calling
release_memory
can be a solution but this only works when anordered_malloc
is used, but I found this to be quite slow. Insteaddeserialize_block ()
uses a pool allocator by default, except when bootstrapping and--fast_bootstrap
is not used, in which case it will just allocate as normal. So anyone using--fast_bootstrap
can benefit from using a memory pool during bootstrapping but the maximum pool size reached will never be deallocated (perhaps this isn't a problem for normal users either?).A helper guard class has been created which wraps all the necessary memory pool clean up functions.