From e362de41963ea790291f88ef6eba5a5f2aa91df4 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Mon, 20 May 2024 21:57:02 +0100 Subject: [PATCH 1/2] Encapsulate bucket setup. --- nano/node/scheduler/buckets.cpp | 24 +++++++++++++++--------- nano/node/scheduler/buckets.hpp | 1 + 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/nano/node/scheduler/buckets.cpp b/nano/node/scheduler/buckets.cpp index c033f6f6ca..2d97ccc4f4 100644 --- a/nano/node/scheduler/buckets.cpp +++ b/nano/node/scheduler/buckets.cpp @@ -25,21 +25,16 @@ void nano::scheduler::buckets::seek () } } -/** - * Prioritization constructor, construct a container containing approximately 'maximum' number of blocks. - * @param maximum number of blocks that this container can hold, this is a soft and approximate limit. - */ -nano::scheduler::buckets::buckets (uint64_t maximum) : - maximum{ maximum } +void nano::scheduler::buckets::setup_buckets (uint64_t maximum) { - auto build_region = [this] (uint128_t const & begin, uint128_t const & end, size_t count) { + auto build_region = [&] (uint128_t const & begin, uint128_t const & end, size_t count) { auto width = (end - begin) / count; for (auto i = 0; i < count; ++i) { minimums.push_back (begin + i * width); } }; - minimums.push_back (uint128_t{ 0 }); + build_region (0, uint128_t{ 1 } << 88, 1); build_region (uint128_t{ 1 } << 88, uint128_t{ 1 } << 92, 2); build_region (uint128_t{ 1 } << 92, uint128_t{ 1 } << 96, 4); build_region (uint128_t{ 1 } << 96, uint128_t{ 1 } << 100, 8); @@ -48,12 +43,23 @@ nano::scheduler::buckets::buckets (uint64_t maximum) : build_region (uint128_t{ 1 } << 108, uint128_t{ 1 } << 112, 8); build_region (uint128_t{ 1 } << 112, uint128_t{ 1 } << 116, 4); build_region (uint128_t{ 1 } << 116, uint128_t{ 1 } << 120, 2); - minimums.push_back (uint128_t{ 1 } << 120); + build_region (uint128_t{ 1 } << 120, uint128_t{ 1 } << 127, 1); + auto bucket_max = std::max (1u, maximum / minimums.size ()); for (size_t i = 0u, n = minimums.size (); i < n; ++i) { buckets_m.push_back (std::make_unique (bucket_max)); } +} + +/** + * Prioritization constructor, construct a container containing approximately 'maximum' number of blocks. + * @param maximum number of blocks that this container can hold, this is a soft and approximate limit. + */ +nano::scheduler::buckets::buckets (uint64_t maximum) : + maximum{ maximum } +{ + setup_buckets (maximum); current = buckets_m.begin (); } diff --git a/nano/node/scheduler/buckets.hpp b/nano/node/scheduler/buckets.hpp index 967b4408f7..94ea88bbaf 100644 --- a/nano/node/scheduler/buckets.hpp +++ b/nano/node/scheduler/buckets.hpp @@ -41,6 +41,7 @@ class buckets final void next (); void seek (); + void setup_buckets (uint64_t maximum); public: buckets (uint64_t maximum = 250000u); From 6a66da923a559382d399262430d1adbb6adb7a4f Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Tue, 21 May 2024 00:33:58 +0100 Subject: [PATCH 2/2] Simplify bucket lookups --- nano/core_test/scheduler_buckets.cpp | 12 ------------ nano/node/scheduler/bucket.cpp | 5 +++-- nano/node/scheduler/bucket.hpp | 5 ++++- nano/node/scheduler/buckets.cpp | 29 ++++++++++++++-------------- nano/node/scheduler/buckets.hpp | 8 ++------ 5 files changed, 24 insertions(+), 35 deletions(-) diff --git a/nano/core_test/scheduler_buckets.cpp b/nano/core_test/scheduler_buckets.cpp index 43912e29b9..dfb68e200a 100644 --- a/nano/core_test/scheduler_buckets.cpp +++ b/nano/core_test/scheduler_buckets.cpp @@ -115,18 +115,6 @@ TEST (buckets, construction) ASSERT_EQ (62, buckets.bucket_count ()); } -TEST (buckets, index_min) -{ - nano::scheduler::buckets buckets; - ASSERT_EQ (0, buckets.index (std::numeric_limits::min ())); -} - -TEST (buckets, index_max) -{ - nano::scheduler::buckets buckets; - ASSERT_EQ (buckets.bucket_count () - 1, buckets.index (std::numeric_limits::max ())); -} - TEST (buckets, insert_Gxrb) { nano::scheduler::buckets buckets; diff --git a/nano/node/scheduler/bucket.cpp b/nano/node/scheduler/bucket.cpp index 51ba1626a5..10aa78fcf2 100644 --- a/nano/node/scheduler/bucket.cpp +++ b/nano/node/scheduler/bucket.cpp @@ -11,8 +11,9 @@ bool nano::scheduler::bucket::value_type::operator== (value_type const & other_a return time == other_a.time && block->hash () == other_a.block->hash (); } -nano::scheduler::bucket::bucket (size_t maximum) : - maximum{ maximum } +nano::scheduler::bucket::bucket (nano::uint128_t minimum_balance, size_t maximum) : + maximum{ maximum }, + minimum_balance{ minimum_balance } { debug_assert (maximum > 0); } diff --git a/nano/node/scheduler/bucket.hpp b/nano/node/scheduler/bucket.hpp index 2f32c17d59..7c5ccfbd6c 100644 --- a/nano/node/scheduler/bucket.hpp +++ b/nano/node/scheduler/bucket.hpp @@ -27,8 +27,11 @@ class bucket final size_t const maximum; public: - bucket (size_t maximum); + bucket (nano::uint128_t minimum_balance, size_t maximum); ~bucket (); + + nano::uint128_t const minimum_balance; + std::shared_ptr top () const; void pop (); void push (uint64_t time, std::shared_ptr block); diff --git a/nano/node/scheduler/buckets.cpp b/nano/node/scheduler/buckets.cpp index 2d97ccc4f4..8d263188ca 100644 --- a/nano/node/scheduler/buckets.cpp +++ b/nano/node/scheduler/buckets.cpp @@ -27,11 +27,13 @@ void nano::scheduler::buckets::seek () void nano::scheduler::buckets::setup_buckets (uint64_t maximum) { + auto const size_expected = 62; + auto bucket_max = std::max (1u, maximum / size_expected); auto build_region = [&] (uint128_t const & begin, uint128_t const & end, size_t count) { auto width = (end - begin) / count; for (auto i = 0; i < count; ++i) { - minimums.push_back (begin + i * width); + buckets_m.push_back (std::make_unique (begin + i * width, bucket_max)); } }; build_region (0, uint128_t{ 1 } << 88, 1); @@ -45,11 +47,7 @@ void nano::scheduler::buckets::setup_buckets (uint64_t maximum) build_region (uint128_t{ 1 } << 116, uint128_t{ 1 } << 120, 2); build_region (uint128_t{ 1 } << 120, uint128_t{ 1 } << 127, 1); - auto bucket_max = std::max (1u, maximum / minimums.size ()); - for (size_t i = 0u, n = minimums.size (); i < n; ++i) - { - buckets_m.push_back (std::make_unique (bucket_max)); - } + debug_assert (buckets_m.size () == size_expected); } /** @@ -67,12 +65,6 @@ nano::scheduler::buckets::~buckets () { } -std::size_t nano::scheduler::buckets::index (nano::uint128_t const & balance) const -{ - auto index = std::upper_bound (minimums.begin (), minimums.end (), balance) - minimums.begin () - 1; - return index; -} - /** * Push a block and its associated time into the prioritization container. * The time is given here because sideband might not exist in the case of state blocks. @@ -80,8 +72,8 @@ std::size_t nano::scheduler::buckets::index (nano::uint128_t const & balance) co void nano::scheduler::buckets::push (uint64_t time, std::shared_ptr block, nano::amount const & priority) { auto was_empty = empty (); - auto & bucket = buckets_m[index (priority.number ())]; - bucket->push (time, block); + auto & bucket = find_bucket (priority.number ()); + bucket.push (time, block); if (was_empty) { seek (); @@ -144,6 +136,15 @@ void nano::scheduler::buckets::dump () const std::cerr << "current: " << current - buckets_m.begin () << '\n'; } +auto nano::scheduler::buckets::find_bucket (nano::uint128_t priority) -> bucket & +{ + auto it = std::upper_bound (buckets_m.begin (), buckets_m.end (), priority, [] (nano::uint128_t const & priority, std::unique_ptr const & bucket) { + return priority < bucket->minimum_balance; + }); + release_assert (it != buckets_m.begin ()); // There should always be a bucket with a minimum_balance of 0 + return **std::prev (it); +} + std::unique_ptr nano::scheduler::buckets::collect_container_info (std::string const & name) { auto composite = std::make_unique (name); diff --git a/nano/node/scheduler/buckets.hpp b/nano/node/scheduler/buckets.hpp index 94ea88bbaf..66d6d22cd8 100644 --- a/nano/node/scheduler/buckets.hpp +++ b/nano/node/scheduler/buckets.hpp @@ -27,11 +27,7 @@ class bucket; class buckets final { /** container for the buckets to be read in round robin fashion */ - std::deque> buckets_m; - - /** thresholds that define the bands for each bucket, the minimum balance an account must have to enter a bucket, - * the container writes a block to the lowest indexed bucket that has balance larger than the bucket's minimum value */ - std::deque minimums; + std::vector> buckets_m; /** index of bucket to read next */ decltype (buckets_m)::const_iterator current; @@ -54,7 +50,7 @@ class buckets final std::size_t bucket_size (std::size_t index) const; bool empty () const; void dump () const; - std::size_t index (nano::uint128_t const & balance) const; + bucket & find_bucket (nano::uint128_t priority); std::unique_ptr collect_container_info (std::string const &); };