Optimize SHAMapItem and leverage new slab allocator: (#4218)

The `SHAMapItem` class contains a variable-sized buffer that
holds the serialized data associated with a particular item
inside a `SHAMap`.

Prior to this commit, the buffer for the serialized data was
allocated separately. Coupled with the fact that most instances
of `SHAMapItem` were wrapped around a `std::shared_ptr` meant
that an instantiation might result in up to three separate
memory allocations.

This commit switches away from `std::shared_ptr` for `SHAMapItem`
and uses `boost::intrusive_ptr` instead, allowing the reference
count for an instance to live inside the instance itself. Coupled
with using a slab-based allocator to optimize memory allocation
for the most commonly sized buffers, the net result is significant
memory savings. In testing, the reduction in memory usage hovers
between 400MB and 650MB. Other scenarios might result in larger
savings.

In performance testing with NFTs, this commit reduces memory size by
about 15% sustained over long duration.

Commit 2 of 3 in #4218.
This commit is contained in:
Nik Bougalis
2023-03-31 21:42:07 -07:00
committed by Elliot Lee
parent b7f588b789
commit c3acbce82d
36 changed files with 293 additions and 178 deletions

View File

@@ -22,7 +22,6 @@
#include <ripple/beast/unit_test.h>
#include <ripple/beast/utility/Journal.h>
#include <ripple/shamap/SHAMap.h>
#include <algorithm>
#include <test/shamap/common.h>
#include <test/unit_test/SuiteJournal.h>
@@ -45,10 +44,7 @@ static_assert(std::is_move_assignable<SHAMap::const_iterator>{}, "");
static_assert(std::is_nothrow_destructible<SHAMapItem>{}, "");
static_assert(!std::is_default_constructible<SHAMapItem>{}, "");
static_assert(std::is_copy_constructible<SHAMapItem>{}, "");
static_assert(std::is_copy_assignable<SHAMapItem>{}, "");
static_assert(std::is_move_constructible<SHAMapItem>{}, "");
static_assert(std::is_move_assignable<SHAMapItem>{}, "");
static_assert(!std::is_copy_constructible<SHAMapItem>{}, "");
static_assert(std::is_nothrow_destructible<SHAMapNodeID>{}, "");
static_assert(std::is_default_constructible<SHAMapNodeID>{}, "");
@@ -155,37 +151,43 @@ public:
if (!backed)
sMap.setUnbacked();
SHAMapItem i1(h1, IntToVUC(1)), i2(h2, IntToVUC(2)),
i3(h3, IntToVUC(3)), i4(h4, IntToVUC(4)), i5(h5, IntToVUC(5));
auto i1 = make_shamapitem(h1, IntToVUC(1));
auto i2 = make_shamapitem(h2, IntToVUC(2));
auto i3 = make_shamapitem(h3, IntToVUC(3));
auto i4 = make_shamapitem(h4, IntToVUC(4));
auto i5 = make_shamapitem(h5, IntToVUC(5));
unexpected(
!sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i2}),
!sMap.addItem(
SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i2)),
"no add");
sMap.invariants();
unexpected(
!sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i1}),
!sMap.addItem(
SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i1)),
"no add");
sMap.invariants();
auto i = sMap.begin();
auto e = sMap.end();
unexpected(i == e || (*i != i1), "bad traverse");
unexpected(i == e || (*i != *i1), "bad traverse");
++i;
unexpected(i == e || (*i != i2), "bad traverse");
unexpected(i == e || (*i != *i2), "bad traverse");
++i;
unexpected(i != e, "bad traverse");
sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i4});
sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i4));
sMap.invariants();
sMap.delItem(i2.key());
sMap.delItem(i2->key());
sMap.invariants();
sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i3});
sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i3));
sMap.invariants();
i = sMap.begin();
e = sMap.end();
unexpected(i == e || (*i != i1), "bad traverse");
unexpected(i == e || (*i != *i1), "bad traverse");
++i;
unexpected(i == e || (*i != i3), "bad traverse");
unexpected(i == e || (*i != *i3), "bad traverse");
++i;
unexpected(i == e || (*i != i4), "bad traverse");
unexpected(i == e || (*i != *i4), "bad traverse");
++i;
unexpected(i != e, "bad traverse");
@@ -265,9 +267,9 @@ public:
BEAST_EXPECT(map.getHash() == beast::zero);
for (int k = 0; k < keys.size(); ++k)
{
SHAMapItem item(keys[k], IntToVUC(k));
BEAST_EXPECT(map.addItem(
SHAMapNodeType::tnTRANSACTION_NM, std::move(item)));
SHAMapNodeType::tnTRANSACTION_NM,
make_shamapitem(keys[k], IntToVUC(k))));
BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
map.invariants();
}
@@ -312,7 +314,7 @@ public:
{
map.addItem(
SHAMapNodeType::tnTRANSACTION_NM,
SHAMapItem{k, IntToVUC(0)});
make_shamapitem(k, IntToVUC(0)));
map.invariants();
}
@@ -346,7 +348,7 @@ class SHAMapPathProof_test : public beast::unit_test::suite
uint256 k(c);
map.addItem(
SHAMapNodeType::tnACCOUNT_STATE,
SHAMapItem{k, Slice{k.data(), k.size()}});
make_shamapitem(k, Slice{k.data(), k.size()}));
map.invariants();
auto root = map.getHash().as_uint256();