From 1efc2e4feeb97bce9e7a997567cf2eba6ddf06db Mon Sep 17 00:00:00 2001 From: Sergey Kroshnin Date: Wed, 4 Mar 2020 23:56:07 +0300 Subject: [PATCH] Update adjusted difficulty in batches (#2604) * Update adjusted difficulty in batches each request loop because ordered roots are used only in this loop. Also prevent extra item modification if adjusted difficulty remains the same (i.e. single block election without dependencies in roots container) * Use new sorting only if mutex lock was removed for frontiers search * Adjust dependent blocks difficulty --- nano/core_test/active_transactions.cpp | 10 +- nano/core_test/conflicts.cpp | 2 + nano/node/active_transactions.cpp | 163 ++++++++++++++----------- nano/node/active_transactions.hpp | 4 +- nano/node/election.cpp | 11 +- nano/node/election.hpp | 1 + 6 files changed, 113 insertions(+), 78 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 4daf82e0..5be7c22d 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -122,6 +122,7 @@ TEST (active_transactions, adjusted_difficulty_priority) // Check adjusted difficulty { nano::lock_guard active_guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); ASSERT_LT (node1.active.roots.find (send2->qualified_root ())->adjusted_difficulty, node1.active.roots.find (send1->qualified_root ())->adjusted_difficulty); ASSERT_LT (node1.active.roots.find (open1->qualified_root ())->adjusted_difficulty, node1.active.roots.find (send1->qualified_root ())->adjusted_difficulty); @@ -168,6 +169,7 @@ TEST (active_transactions, adjusted_difficulty_priority) // Check adjusted difficulty nano::lock_guard lock (node1.active.mutex); + node1.active.update_adjusted_difficulty (); uint64_t last_adjusted (0); for (auto i (node1.active.roots.get<1> ().begin ()), n (node1.active.roots.get<1> ().end ()); i != n; ++i) { @@ -223,7 +225,8 @@ TEST (active_transactions, adjusted_difficulty_overflow_max) modify_difficulty (send2_root); modify_difficulty (open1_root); modify_difficulty (open2_root); - node1.active.adjust_difficulty (send2->hash ()); + node1.active.add_adjust_difficulty (send2->hash ()); + node1.active.update_adjusted_difficulty (); // Test overflow ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); ASSERT_EQ (send1_root->adjusted_difficulty, std::numeric_limits::max ()); @@ -277,7 +280,8 @@ TEST (active_transactions, adjusted_difficulty_overflow_min) modify_difficulty (open1_root); modify_difficulty (open2_root); modify_difficulty (send3_root); - node1.active.adjust_difficulty (send1->hash ()); + node1.active.add_adjust_difficulty (send1->hash ()); + node1.active.update_adjusted_difficulty (); // Test overflow ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); ASSERT_EQ (send1_root->adjusted_difficulty, std::numeric_limits::min () + 3); @@ -399,6 +403,8 @@ TEST (active_transactions, prioritize_chains) } size_t seen (0); { + nano::lock_guard active_guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); auto it (node1.active.roots.get<1> ().begin ()); while (!node1.active.roots.empty () && it != node1.active.roots.get<1> ().end ()) { diff --git a/nano/core_test/conflicts.cpp b/nano/core_test/conflicts.cpp index 33094355..1cf633a3 100644 --- a/nano/core_test/conflicts.cpp +++ b/nano/core_test/conflicts.cpp @@ -253,6 +253,7 @@ TEST (conflicts, adjusted_difficulty) std::unordered_map adjusted_difficulties; { nano::lock_guard guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ()); for (auto i (node1.active.roots.get<1> ().begin ()), n (node1.active.roots.get<1> ().end ()); i != n; ++i) { @@ -285,6 +286,7 @@ TEST (conflicts, adjusted_difficulty) } { nano::lock_guard guard (node1.active.mutex); + node1.active.update_adjusted_difficulty (); ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), open_epoch2->hash ()); } } diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index a1fc3298..6161501d 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -222,14 +222,13 @@ void nano::active_transactions::request_confirm (nano::unique_lock & lock_a.unlock (); search_frontiers (transaction_l); lock_a.lock (); + update_adjusted_difficulty (); // New roots sorting } } // Only representatives ready to receive batched confirm_req - lock_a.unlock (); nano::confirmation_solicitor solicitor (node.network, node.network_params.network); solicitor.prepare (node.rep_crawler.representatives (node.network_params.protocol.tcp_realtime_protocol_version_min)); - lock_a.lock (); auto election_ttl_cutoff_l (std::chrono::steady_clock::now () - election_time_to_live); auto roots_size_l (roots.size ()); @@ -281,6 +280,7 @@ void nano::active_transactions::request_loop () // Account for the time spent in request_confirm by defining the wakeup point beforehand const auto wakeup_l (std::chrono::steady_clock::now () + std::chrono::milliseconds (node.network_params.network.request_interval_ms)); + update_adjusted_difficulty (); update_active_difficulty (lock); request_confirm (lock); @@ -484,7 +484,7 @@ std::pair, bool> nano::active_transactions::inse auto difficulty (block_a->difficulty ()); roots.get ().emplace (nano::conflict_info{ root, difficulty, difficulty, result.first }); blocks.emplace (hash, result.first); - adjust_difficulty (hash); + add_adjust_difficulty (hash); result.first->insert_inactive_votes_cache (hash); } } @@ -601,97 +601,111 @@ void nano::active_transactions::update_difficulty (std::shared_ptr info_a.difficulty = difficulty; }); existing_election->election->publish (block_a); - adjust_difficulty (block_a->hash ()); + add_adjust_difficulty (block_a->hash ()); } } } -void nano::active_transactions::adjust_difficulty (nano::block_hash const & hash_a) +void nano::active_transactions::add_adjust_difficulty (nano::block_hash const & hash_a) +{ + debug_assert (!mutex.try_lock ()); + adjust_difficulty_list.push_back (hash_a); +} + +void nano::active_transactions::update_adjusted_difficulty () { debug_assert (!mutex.try_lock ()); - std::deque> remaining_blocks; - remaining_blocks.emplace_back (hash_a, 0); std::unordered_set processed_blocks; - std::vector> elections_list; - double sum (0.); - int64_t highest_level (0); - int64_t lowest_level (0); - while (!remaining_blocks.empty ()) + while (!adjust_difficulty_list.empty ()) { - auto const & item (remaining_blocks.front ()); - auto hash (item.first); - auto level (item.second); - if (processed_blocks.find (hash) == processed_blocks.end ()) + auto const & adjust_difficulty_item (adjust_difficulty_list.front ()); + std::deque> remaining_blocks; + remaining_blocks.emplace_back (adjust_difficulty_item, 0); + adjust_difficulty_list.pop_front (); + std::vector> elections_list; + double sum (0.); + int64_t highest_level (0); + int64_t lowest_level (0); + while (!remaining_blocks.empty ()) { - auto existing (blocks.find (hash)); - if (existing != blocks.end () && !existing->second->confirmed () && existing->second->status.winner->hash () == hash) + auto const & item (remaining_blocks.front ()); + auto hash (item.first); + auto level (item.second); + if (processed_blocks.find (hash) == processed_blocks.end ()) { - auto previous (existing->second->status.winner->previous ()); - if (!previous.is_zero ()) + auto existing (blocks.find (hash)); + if (existing != blocks.end () && !existing->second->confirmed () && existing->second->status.winner->hash () == hash) { - remaining_blocks.emplace_back (previous, level + 1); - } - auto source (existing->second->status.winner->source ()); - if (!source.is_zero () && source != previous) - { - remaining_blocks.emplace_back (source, level + 1); - } - auto link (existing->second->status.winner->link ()); - if (!link.is_zero () && !node.ledger.is_epoch_link (link) && link != previous) - { - remaining_blocks.emplace_back (link, level + 1); - } - for (auto & dependent_block : existing->second->dependent_blocks) - { - remaining_blocks.emplace_back (dependent_block, level - 1); - } - processed_blocks.insert (hash); - nano::qualified_root root (previous, existing->second->status.winner->root ()); - auto existing_root (roots.get ().find (root)); - if (existing_root != roots.get ().end ()) - { - sum += nano::difficulty::to_multiplier (existing_root->difficulty, node.network_params.network.publish_threshold); - elections_list.emplace_back (root, level); - if (level > highest_level) + auto previous (existing->second->status.winner->previous ()); + if (!previous.is_zero ()) { - highest_level = level; + remaining_blocks.emplace_back (previous, level + 1); } - else if (level < lowest_level) + auto source (existing->second->status.winner->source ()); + if (!source.is_zero () && source != previous) { - lowest_level = level; + remaining_blocks.emplace_back (source, level + 1); + } + auto link (existing->second->status.winner->link ()); + if (!link.is_zero () && !node.ledger.is_epoch_link (link) && link != previous) + { + remaining_blocks.emplace_back (link, level + 1); + } + for (auto & dependent_block : existing->second->dependent_blocks) + { + remaining_blocks.emplace_back (dependent_block, level - 1); + } + processed_blocks.insert (hash); + nano::qualified_root root (previous, existing->second->status.winner->root ()); + auto existing_root (roots.get ().find (root)); + if (existing_root != roots.get ().end ()) + { + sum += nano::difficulty::to_multiplier (existing_root->difficulty, node.network_params.network.publish_threshold); + elections_list.emplace_back (root, level); + if (level > highest_level) + { + highest_level = level; + } + else if (level < lowest_level) + { + lowest_level = level; + } } } } + remaining_blocks.pop_front (); } - remaining_blocks.pop_front (); - } - if (!elections_list.empty ()) - { - double multiplier = sum / elections_list.size (); - uint64_t average = nano::difficulty::from_multiplier (multiplier, node.network_params.network.publish_threshold); - // Prevent overflow - int64_t limiter (0); - if (std::numeric_limits::max () - average < static_cast (highest_level)) + if (!elections_list.empty ()) { - // Highest adjusted difficulty value should be std::numeric_limits::max () - limiter = std::numeric_limits::max () - average + highest_level; - debug_assert (std::numeric_limits::max () == average + highest_level - limiter); - } - else if (average < std::numeric_limits::min () - lowest_level) - { - // Lowest adjusted difficulty value should be std::numeric_limits::min () - limiter = std::numeric_limits::min () - average + lowest_level; - debug_assert (std::numeric_limits::min () == average + lowest_level - limiter); - } + double multiplier = sum / elections_list.size (); + uint64_t average = nano::difficulty::from_multiplier (multiplier, node.network_params.network.publish_threshold); + // Prevent overflow + int64_t limiter (0); + if (std::numeric_limits::max () - average < static_cast (highest_level)) + { + // Highest adjusted difficulty value should be std::numeric_limits::max () + limiter = std::numeric_limits::max () - average + highest_level; + debug_assert (std::numeric_limits::max () == average + highest_level - limiter); + } + else if (average < std::numeric_limits::min () - lowest_level) + { + // Lowest adjusted difficulty value should be std::numeric_limits::min () + limiter = std::numeric_limits::min () - average + lowest_level; + debug_assert (std::numeric_limits::min () == average + lowest_level - limiter); + } - // Set adjusted difficulty - for (auto & item : elections_list) - { - auto existing_root (roots.get ().find (item.first)); - uint64_t difficulty_a = average + item.second - limiter; - roots.get ().modify (existing_root, [difficulty_a](nano::conflict_info & info_a) { - info_a.adjusted_difficulty = difficulty_a; - }); + // Set adjusted difficulty + for (auto & item : elections_list) + { + auto existing_root (roots.get ().find (item.first)); + uint64_t difficulty_a = average + item.second - limiter; + if (existing_root->adjusted_difficulty != difficulty_a) + { + roots.get ().modify (existing_root, [difficulty_a](nano::conflict_info & info_a) { + info_a.adjusted_difficulty = difficulty_a; + }); + } + } } } } @@ -778,6 +792,7 @@ void nano::active_transactions::erase (nano::block const & block_a) if (root_it != roots.get ().end ()) { root_it->election->clear_blocks (); + root_it->election->adjust_dependent_difficulty (); roots.get ().erase (root_it); node.logger.try_log (boost::str (boost::format ("Election erased for block block %1% root %2%") % block_a.hash ().to_string () % block_a.root ().to_string ())); } diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 6a5b58c0..d1ffcb3d 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -95,7 +95,8 @@ public: bool active (nano::qualified_root const &); std::shared_ptr election (nano::qualified_root const &) const; void update_difficulty (std::shared_ptr); - void adjust_difficulty (nano::block_hash const &); + void add_adjust_difficulty (nano::block_hash const &); + void update_adjusted_difficulty (); void update_active_difficulty (nano::unique_lock &); uint64_t active_difficulty (); uint64_t limited_active_difficulty (); @@ -181,6 +182,7 @@ private: void prioritize_account_for_confirmation (prioritize_num_uncemented &, size_t &, nano::account const &, nano::account_info const &, uint64_t); static size_t constexpr max_priority_cementable_frontiers{ 100000 }; static size_t constexpr confirmed_frontiers_max_pending_cut_off{ 1000 }; + std::deque adjust_difficulty_list; // clang-format off using ordered_cache = boost::multi_index_containerprocess_confirmed (status_l, this_l); confirmation_action_l (status_l.winner); }); + adjust_dependent_difficulty (); } } @@ -367,7 +368,7 @@ void nano::election::confirm_if_quorum () node.block_processor.force (block_l); status.winner = block_l; update_dependent (); - node.active.adjust_difficulty (winner_hash_l); + node.active.add_adjust_difficulty (winner_hash_l); } if (have_quorum (tally_l, sum)) { @@ -522,6 +523,14 @@ void nano::election::update_dependent () } } +void nano::election::adjust_dependent_difficulty () +{ + for (auto & dependent_block : dependent_blocks) + { + node.active.add_adjust_difficulty (dependent_block); + } +} + void nano::election::clear_blocks () { auto winner_hash (status.winner->hash ()); diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 9339cc2b..6ea73c11 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -78,6 +78,7 @@ public: bool publish (std::shared_ptr block_a); size_t last_votes_size (); void update_dependent (); + void adjust_dependent_difficulty (); void clear_blocks (); void insert_inactive_votes_cache (nano::block_hash const &);