Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 7 additions & 17 deletions category/mpt/detail/collected_stats.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,17 @@
#include <category/mpt/config.hpp>

#include <cstdint>
#include <new>
#include <type_traits>

MONAD_MPT_NAMESPACE_BEGIN

// Turn on to collect stats
#define MONAD_MPT_COLLECT_STATS 1

namespace detail
{
// Lifetime totals, never reset. Every field must be monotonically
// increasing: delta_since() subtracts fieldwise to recover what a single
// upsert contributed.
struct TrieUpdateCollectedStats
{
#ifdef MONAD_MPT_COLLECT_STATS
// counters
uint64_t nodes_created_or_updated{0};
// reads stats
Expand All @@ -52,28 +50,20 @@ namespace detail
// Sum of the following three equals the current block slow ring
// growth
uint64_t compacted_bytes_in_fast{0}; // copied from fast to slow
// Doubles as the GC-efficiency input to advance_compact_offsets, which
// makes it load-bearing state and not just observability.
uint64_t compacted_bytes_in_slow{0}; // copied from slow to slow
uint64_t bytes_copied_slow_to_fast_for_slow{0};

// expire stats
uint64_t nodes_updated_expire{0};
uint64_t nreads_expire{0};
#else
uint64_t compacted_bytes_in_slow{0};
#endif

void reset()
{
this->~TrieUpdateCollectedStats();
new (this) TrieUpdateCollectedStats();
}
TrieUpdateCollectedStats
delta_since(TrieUpdateCollectedStats const &base) const noexcept;
};

#ifdef MONAD_MPT_COLLECT_STATS
static_assert(sizeof(TrieUpdateCollectedStats) == 160);
#else
static_assert(sizeof(TrieUpdateCollectedStats) == 8);
#endif
static_assert(alignof(TrieUpdateCollectedStats) == 8);
static_assert(std::is_trivially_copyable_v<TrieUpdateCollectedStats>);
}
Expand Down
61 changes: 61 additions & 0 deletions category/mpt/test/compaction_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,64 @@ TEST_F(CompactionTest, first_chunk_is_compacted)
// TODO DO COMPACTION
// TODO CHECK POOL'S FIRST CHUNK WAS DEFINITELY RELEASED
}

TEST_F(CompactionTest, last_upsert_stats_isolates_the_latest_upsert)
{
auto &aux = state()->aux;
// The fixture has already run many upserts, so the lifetime totals start
// well above zero, which is what makes the isolation checks meaningful.
ASSERT_GT(aux.stats_snapshot().nodes_created_or_updated, 0u);

auto const erase_keys = [&](size_t const first, size_t const count) {
std::vector<Update> updates;
updates.reserve(count);
for (size_t i = 0; i < count; ++i) {
updates.push_back(
make_update(state()->keys[first + i].first, UpdateList{}));
}
UpdateList ls;
for (auto &update : updates) {
ls.push_front(update);
}
// compaction=false also pins that the per-upsert delta advances on
// upserts that never reach the compaction code.
state()->root = aux.do_update(
std::move(state()->root),
state()->sm,
std::move(ls),
state()->version++,
/*compaction=*/false,
/*can_write_to_fast=*/true,
/*write_root=*/true,
timeline_id::primary);
};

auto const lifetime_before = aux.stats_snapshot();
erase_keys(0, 4);
auto const lifetime_first = aux.stats_snapshot();
auto const delta_first = aux.last_upsert_stats();
erase_keys(4, 4);
auto const lifetime_second = aux.stats_snapshot();
auto const delta_second = aux.last_upsert_stats();

// Each delta is exactly the lifetime growth across its own upsert.
EXPECT_EQ(
lifetime_first.nodes_created_or_updated -
lifetime_before.nodes_created_or_updated,
delta_first.nodes_created_or_updated);
EXPECT_EQ(
lifetime_second.nodes_created_or_updated -
lifetime_first.nodes_created_or_updated,
delta_second.nodes_created_or_updated);

// The lifetime totals only ever grow, and a delta stays strictly below
// them: a reintroduced reset breaks the first property, handing a consumer
// the lifetime totals instead of the delta breaks the second.
EXPECT_GE(
lifetime_second.nodes_created_or_updated,
lifetime_first.nodes_created_or_updated);
EXPECT_GT(delta_second.nodes_created_or_updated, 0u);
EXPECT_LT(
delta_second.nodes_created_or_updated,
lifetime_second.nodes_created_or_updated);
}
1 change: 0 additions & 1 deletion category/mpt/trie.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ Node::SharedPtr upsert(
Node::SharedPtr old, UpdateList &&updates, bool const write_root,
timeline_id const tid)
{
aux.reset_stats();
auto sentinel = make_tnode(1 /*mask*/);
ChildData &entry = sentinel->children[0];
sentinel->children[0] = ChildData{.branch = 0};
Expand Down
28 changes: 23 additions & 5 deletions category/mpt/trie.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,13 @@ class UpdateAux
bool alternate_slow_fast_writer_{false};
bool can_write_to_fast_{true};

// Lifetime totals, never reset. The per-upsert figures that the stats log
// and the slow-ring speed control consume are the difference between
// consecutive snapshots.
detail::TrieUpdateCollectedStats stats_;
detail::TrieUpdateCollectedStats prev_upsert_stats_;
detail::TrieUpdateCollectedStats last_upsert_stats_;

public:
// Allocate the first cnv chunk for db metadata copies
static constexpr unsigned cnv_chunks_for_db_metadata = 1;
Expand All @@ -253,8 +260,6 @@ class UpdateAux
node_writer_unique_ptr_type node_writer_fast{};
node_writer_unique_ptr_type node_writer_slow{};

detail::TrieUpdateCollectedStats stats;

// in-memory
UpdateAux() = default;

Expand All @@ -281,8 +286,19 @@ class UpdateAux
void move_trie_version_forward(
uint64_t src, uint64_t dest, timeline_id tid = timeline_id::primary);

// Lifetime totals, monotonically increasing across every upsert.
detail::TrieUpdateCollectedStats stats_snapshot() const noexcept
{
return stats_;
}

// What the most recent upsert alone contributed.
detail::TrieUpdateCollectedStats last_upsert_stats() const noexcept
{
return last_upsert_stats_;
}

// collect and print trie update stats
void reset_stats();
void collect_expire_stats(bool is_read);
void collect_number_nodes_created_stats();
void collect_compaction_read_stats(
Expand All @@ -292,7 +308,9 @@ class UpdateAux
virtual_chunk_offset_t node_offset, uint32_t node_disk_size,
timeline_id tid);

void print_update_stats(uint64_t version, timeline_id tid);
void print_update_stats(
uint64_t version, timeline_id tid,
detail::TrieUpdateCollectedStats const &upsert_stats);

using chunk_list = DbMetadataContext::chunk_list;

Expand Down Expand Up @@ -378,7 +396,7 @@ class UpdateAux
};

static_assert(
sizeof(UpdateAux) == 120 + sizeof(detail::TrieUpdateCollectedStats));
sizeof(UpdateAux) == 120 + 3 * sizeof(detail::TrieUpdateCollectedStats));
static_assert(alignof(UpdateAux) == 8);

template <receiver Receiver>
Expand Down
Loading
Loading