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
This commit is contained in:
Wesley Shillingford 2019-05-25 15:39:37 +01:00 committed by GitHub
commit f9021b36e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 87 additions and 34 deletions

View file

@ -1,4 +1,6 @@
#include "gtest/gtest.h"
#include <nano/lib/blocks.hpp>
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 ();

View file

@ -4,6 +4,7 @@
#include <nano/lib/utility.hpp>
#include <boost/endian/conversion.hpp>
#include <boost/pool/singleton_pool.hpp>
/** 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<nano::block, T>::value, "Input parameter is not a block type");
return (first.type () == second.type ()) && (static_cast<T const &> (second)) == first;
}
template <typename tag, typename block_type>
using pool = boost::singleton_pool<tag, sizeof (block_type)>;
template <typename tag, typename block_type>
struct pool_deleter
{
void operator() (block_type * p) const
{
pool<tag, block_type>::free (p);
}
};
template <typename tag, typename block_type>
std::shared_ptr<block_type> deserialize_block_from_pool (nano::stream & stream_a)
{
bool error (false);
auto obj_raw = static_cast<block_type *> (pool<tag, block_type>::malloc ());
new (obj_raw) block_type (error, stream_a);
if (!error)
{
return std::shared_ptr<block_type> (obj_raw, pool_deleter<tag, block_type>{});
}
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<open_block_tag, nano::open_block>::purge_memory ();
pool<state_block_tag, nano::state_block>::purge_memory ();
pool<send_block_tag, nano::send_block>::purge_memory ();
pool<change_or_receive_block_tag, nano::change_block>::purge_memory ();
}
std::string nano::block::to_json () const
@ -1236,7 +1291,7 @@ std::shared_ptr<nano::block> nano::deserialize_block_json (boost::property_tree:
return result;
}
std::shared_ptr<nano::block> nano::deserialize_block (nano::stream & stream_a, nano::block_uniquer * uniquer_a)
std::shared_ptr<nano::block> 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::block> nano::deserialize_block (nano::stream & stream_a, n
{
case nano::block_type::receive:
{
bool error (false);
std::unique_ptr<nano::receive_block> obj (new nano::receive_block (error, stream_a));
if (!error)
{
result = std::move (obj);
}
result = deserialize_block_from_pool<change_or_receive_block_tag, nano::receive_block> (stream_a);
break;
}
case nano::block_type::send:
{
bool error (false);
std::unique_ptr<nano::send_block> obj (new nano::send_block (error, stream_a));
if (!error)
{
result = std::move (obj);
}
result = deserialize_block_from_pool<send_block_tag, nano::send_block> (stream_a);
break;
}
case nano::block_type::open:
{
bool error (false);
std::unique_ptr<nano::open_block> obj (new nano::open_block (error, stream_a));
if (!error)
{
result = std::move (obj);
}
result = deserialize_block_from_pool<open_block_tag, nano::open_block> (stream_a);
break;
}
case nano::block_type::change:
{
bool error (false);
std::unique_ptr<nano::change_block> obj (new nano::change_block (error, stream_a));
if (!error)
{
result = std::move (obj);
}
result = deserialize_block_from_pool<change_or_receive_block_tag, nano::change_block> (stream_a);
break;
}
case nano::block_type::state:
{
bool error (false);
std::unique_ptr<nano::state_block> obj (new nano::state_block (error, stream_a));
if (!error)
{
result = std::move (obj);
}
result = deserialize_block_from_pool<state_block_tag, nano::state_block> (stream_a);
break;
}
default:

View file

@ -356,8 +356,17 @@ private:
std::unique_ptr<seq_con_info_component> collect_seq_con_info (block_uniquer & block_uniquer, const std::string & name);
std::shared_ptr<nano::block> deserialize_block (nano::stream &, nano::block_uniquer * = nullptr);
std::shared_ptr<nano::block> deserialize_block (nano::stream &);
std::shared_ptr<nano::block> deserialize_block (nano::stream &, nano::block_type, nano::block_uniquer * = nullptr);
std::shared_ptr<nano::block> 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 ();
};
}

View file

@ -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);

View file

@ -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<char **> (argv));

View file

@ -1763,6 +1763,7 @@ void nano::wallets::stop ()
{
thread.join ();
}
watcher.stop ();
}
nano::write_transaction nano::wallets::tx_begin_write ()

View file

@ -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");

View file

@ -1,3 +1,5 @@
#include <nano/lib/blocks.hpp>
#include <gtest/gtest.h>
#include <QApplication>
@ -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);

View file

@ -1,3 +1,5 @@
#include <nano/lib/blocks.hpp>
#include <gtest/gtest.h>
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 ();

View file

@ -1,3 +1,5 @@
#include <nano/lib/blocks.hpp>
#include <gtest/gtest.h>
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 ();