From f9021b36e758429d65e408e417d9acf86554393c Mon Sep 17 00:00:00 2001 From: Wesley Shillingford Date: Sat, 25 May 2019 15:39:37 +0100 Subject: [PATCH] Use a memory pool to reduce block deserialization heap usage (#2025) * Use a memory pool when deserializing blocks to reduce heap usage * Stop work watcher * Purge memory at end of main * Formatting * Forgot to add it to add purging in rpc_test * Formatting --- nano/core_test/core_test_main.cc | 3 ++ nano/lib/blocks.cpp | 92 +++++++++++++++++++++----------- nano/lib/blocks.hpp | 11 +++- nano/nano_node/entry.cpp | 2 +- nano/nano_wallet/entry.cpp | 2 +- nano/node/wallet.cpp | 1 + nano/qt_system/entry.cpp | 1 + nano/qt_test/entry.cpp | 3 ++ nano/rpc_test/entry.cpp | 3 ++ nano/slow_test/entry.cpp | 3 ++ 10 files changed, 87 insertions(+), 34 deletions(-) diff --git a/nano/core_test/core_test_main.cc b/nano/core_test/core_test_main.cc index a7ebb4d8..8c5834f2 100644 --- a/nano/core_test/core_test_main.cc +++ b/nano/core_test/core_test_main.cc @@ -1,4 +1,6 @@ #include "gtest/gtest.h" + +#include namespace nano { void cleanup_test_directories_on_exit (); @@ -8,6 +10,7 @@ GTEST_API_ int main (int argc, char ** argv) { printf ("Running main() from core_test_main.cc\n"); nano::force_nano_test_network (); + nano::block_memory_pool_cleanup_guard block_memory_pool_cleanup_guard; testing::InitGoogleTest (&argc, argv); auto res = RUN_ALL_TESTS (); nano::cleanup_test_directories_on_exit (); diff --git a/nano/lib/blocks.cpp b/nano/lib/blocks.cpp index 58462021..88fa180e 100644 --- a/nano/lib/blocks.cpp +++ b/nano/lib/blocks.cpp @@ -4,6 +4,7 @@ #include #include +#include /** Compare blocks, first by type, then content. This is an optimization over dynamic_cast, which is very slow on some platforms. */ namespace @@ -14,6 +15,60 @@ bool blocks_equal (T const & first, nano::block const & second) static_assert (std::is_base_of::value, "Input parameter is not a block type"); return (first.type () == second.type ()) && (static_cast (second)) == first; } + +template +using pool = boost::singleton_pool; + +template +struct pool_deleter +{ + void operator() (block_type * p) const + { + pool::free (p); + } +}; + +template +std::shared_ptr deserialize_block_from_pool (nano::stream & stream_a) +{ + bool error (false); + auto obj_raw = static_cast (pool::malloc ()); + new (obj_raw) block_type (error, stream_a); + if (!error) + { + return std::shared_ptr (obj_raw, pool_deleter{}); + } + return nullptr; +} + +struct state_block_tag +{ +}; +struct send_block_tag +{ +}; +struct open_block_tag +{ +}; +struct change_or_receive_block_tag +{ +}; + +// For efficiency as change and receive blocks currently have the same size they share the same memory pool +static_assert (nano::change_block::size == nano::receive_block::size, "Change and receive blocks not longer the same size, a new pool is required"); +} + +nano::block_memory_pool_cleanup_guard::~block_memory_pool_cleanup_guard () +{ + purge (); +} + +void nano::block_memory_pool_cleanup_guard::purge () +{ + pool::purge_memory (); + pool::purge_memory (); + pool::purge_memory (); + pool::purge_memory (); } std::string nano::block::to_json () const @@ -1236,7 +1291,7 @@ std::shared_ptr nano::deserialize_block_json (boost::property_tree: return result; } -std::shared_ptr nano::deserialize_block (nano::stream & stream_a, nano::block_uniquer * uniquer_a) +std::shared_ptr nano::deserialize_block (nano::stream & stream_a) { nano::block_type type; auto error (try_read (stream_a, type)); @@ -1255,52 +1310,27 @@ std::shared_ptr nano::deserialize_block (nano::stream & stream_a, n { case nano::block_type::receive: { - bool error (false); - std::unique_ptr obj (new nano::receive_block (error, stream_a)); - if (!error) - { - result = std::move (obj); - } + result = deserialize_block_from_pool (stream_a); break; } case nano::block_type::send: { - bool error (false); - std::unique_ptr obj (new nano::send_block (error, stream_a)); - if (!error) - { - result = std::move (obj); - } + result = deserialize_block_from_pool (stream_a); break; } case nano::block_type::open: { - bool error (false); - std::unique_ptr obj (new nano::open_block (error, stream_a)); - if (!error) - { - result = std::move (obj); - } + result = deserialize_block_from_pool (stream_a); break; } case nano::block_type::change: { - bool error (false); - std::unique_ptr obj (new nano::change_block (error, stream_a)); - if (!error) - { - result = std::move (obj); - } + result = deserialize_block_from_pool (stream_a); break; } case nano::block_type::state: { - bool error (false); - std::unique_ptr obj (new nano::state_block (error, stream_a)); - if (!error) - { - result = std::move (obj); - } + result = deserialize_block_from_pool (stream_a); break; } default: diff --git a/nano/lib/blocks.hpp b/nano/lib/blocks.hpp index 052ebcc2..74ce0bc3 100644 --- a/nano/lib/blocks.hpp +++ b/nano/lib/blocks.hpp @@ -356,8 +356,17 @@ private: std::unique_ptr collect_seq_con_info (block_uniquer & block_uniquer, const std::string & name); -std::shared_ptr deserialize_block (nano::stream &, nano::block_uniquer * = nullptr); +std::shared_ptr deserialize_block (nano::stream &); std::shared_ptr deserialize_block (nano::stream &, nano::block_type, nano::block_uniquer * = nullptr); std::shared_ptr deserialize_block_json (boost::property_tree::ptree const &, nano::block_uniquer * = nullptr); void serialize_block (nano::stream &, nano::block const &); + +class block_memory_pool_cleanup_guard final +{ +public: + ~block_memory_pool_cleanup_guard (); + +private: + static void purge (); +}; } diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 344eda05..b7da6c80 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -59,7 +59,7 @@ void update_flags (nano::node_flags & flags_a, boost::program_options::variables int main (int argc, char * const * argv) { nano::set_umask (); - + nano::block_memory_pool_cleanup_guard block_memory_pool_cleanup_guard; boost::program_options::options_description description ("Command line options"); nano::add_node_options (description); diff --git a/nano/nano_wallet/entry.cpp b/nano/nano_wallet/entry.cpp index ddb5d695..ccc6fd67 100644 --- a/nano/nano_wallet/entry.cpp +++ b/nano/nano_wallet/entry.cpp @@ -372,7 +372,7 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost int main (int argc, char * const * argv) { nano::set_umask (); - + nano::block_memory_pool_cleanup_guard block_memory_pool_cleanup_guard; try { QApplication application (argc, const_cast (argv)); diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 67a1c541..01c0312d 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -1763,6 +1763,7 @@ void nano::wallets::stop () { thread.join (); } + watcher.stop (); } nano::write_transaction nano::wallets::tx_begin_write () diff --git a/nano/qt_system/entry.cpp b/nano/qt_system/entry.cpp index a2463cd2..03ddd14c 100644 --- a/nano/qt_system/entry.cpp +++ b/nano/qt_system/entry.cpp @@ -8,6 +8,7 @@ int main (int argc, char ** argv) { nano::network_constants::set_active_network (nano::nano_networks::nano_test_network); + nano::block_memory_pool_cleanup_guard block_memory_pool_cleanup_guard; QApplication application (argc, argv); QCoreApplication::setOrganizationName ("Nano"); QCoreApplication::setOrganizationDomain ("nano.org"); diff --git a/nano/qt_test/entry.cpp b/nano/qt_test/entry.cpp index 22a1df3c..cee8446b 100644 --- a/nano/qt_test/entry.cpp +++ b/nano/qt_test/entry.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -11,6 +13,7 @@ void force_nano_test_network (); int main (int argc, char ** argv) { nano::force_nano_test_network (); + nano::block_memory_pool_cleanup_guard block_memory_pool_cleanup_guard; QApplication application (argc, argv); test_application = &application; testing::InitGoogleTest (&argc, argv); diff --git a/nano/rpc_test/entry.cpp b/nano/rpc_test/entry.cpp index 24a21a74..f45abaef 100644 --- a/nano/rpc_test/entry.cpp +++ b/nano/rpc_test/entry.cpp @@ -1,3 +1,5 @@ +#include + #include namespace nano { @@ -8,6 +10,7 @@ void force_nano_test_network (); int main (int argc, char ** argv) { nano::force_nano_test_network (); + nano::block_memory_pool_cleanup_guard block_memory_pool_cleanup_guard; testing::InitGoogleTest (&argc, argv); auto res = RUN_ALL_TESTS (); nano::cleanup_test_directories_on_exit (); diff --git a/nano/slow_test/entry.cpp b/nano/slow_test/entry.cpp index 24a21a74..f45abaef 100644 --- a/nano/slow_test/entry.cpp +++ b/nano/slow_test/entry.cpp @@ -1,3 +1,5 @@ +#include + #include namespace nano { @@ -8,6 +10,7 @@ void force_nano_test_network (); int main (int argc, char ** argv) { nano::force_nano_test_network (); + nano::block_memory_pool_cleanup_guard block_memory_pool_cleanup_guard; testing::InitGoogleTest (&argc, argv); auto res = RUN_ALL_TESTS (); nano::cleanup_test_directories_on_exit ();