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
This commit is contained in:
parent
695dc5f5dc
commit
1efc2e4fee
6 changed files with 113 additions and 78 deletions
|
@ -122,6 +122,7 @@ TEST (active_transactions, adjusted_difficulty_priority)
|
|||
// Check adjusted difficulty
|
||||
{
|
||||
nano::lock_guard<std::mutex> 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<std::mutex> 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<std::uint64_t>::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<std::uint64_t>::min () + 3);
|
||||
|
@ -399,6 +403,8 @@ TEST (active_transactions, prioritize_chains)
|
|||
}
|
||||
size_t seen (0);
|
||||
{
|
||||
nano::lock_guard<std::mutex> 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 ())
|
||||
{
|
||||
|
|
|
@ -253,6 +253,7 @@ TEST (conflicts, adjusted_difficulty)
|
|||
std::unordered_map<nano::block_hash, uint64_t> adjusted_difficulties;
|
||||
{
|
||||
nano::lock_guard<std::mutex> 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<std::mutex> guard (node1.active.mutex);
|
||||
node1.active.update_adjusted_difficulty ();
|
||||
ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), open_epoch2->hash ());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -222,14 +222,13 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
|
|||
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<std::shared_ptr<nano::election>, bool> nano::active_transactions::inse
|
|||
auto difficulty (block_a->difficulty ());
|
||||
roots.get<tag_root> ().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<nano::block>
|
|||
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<std::pair<nano::block_hash, int64_t>> remaining_blocks;
|
||||
remaining_blocks.emplace_back (hash_a, 0);
|
||||
std::unordered_set<nano::block_hash> processed_blocks;
|
||||
std::vector<std::pair<nano::qualified_root, int64_t>> 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<std::pair<nano::block_hash, int64_t>> remaining_blocks;
|
||||
remaining_blocks.emplace_back (adjust_difficulty_item, 0);
|
||||
adjust_difficulty_list.pop_front ();
|
||||
std::vector<std::pair<nano::qualified_root, int64_t>> 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<tag_root> ().find (root));
|
||||
if (existing_root != roots.get<tag_root> ().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<tag_root> ().find (root));
|
||||
if (existing_root != roots.get<tag_root> ().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<std::uint64_t>::max () - average < static_cast<uint64_t> (highest_level))
|
||||
if (!elections_list.empty ())
|
||||
{
|
||||
// Highest adjusted difficulty value should be std::numeric_limits<std::uint64_t>::max ()
|
||||
limiter = std::numeric_limits<std::uint64_t>::max () - average + highest_level;
|
||||
debug_assert (std::numeric_limits<std::uint64_t>::max () == average + highest_level - limiter);
|
||||
}
|
||||
else if (average < std::numeric_limits<std::uint64_t>::min () - lowest_level)
|
||||
{
|
||||
// Lowest adjusted difficulty value should be std::numeric_limits<std::uint64_t>::min ()
|
||||
limiter = std::numeric_limits<std::uint64_t>::min () - average + lowest_level;
|
||||
debug_assert (std::numeric_limits<std::uint64_t>::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<std::uint64_t>::max () - average < static_cast<uint64_t> (highest_level))
|
||||
{
|
||||
// Highest adjusted difficulty value should be std::numeric_limits<std::uint64_t>::max ()
|
||||
limiter = std::numeric_limits<std::uint64_t>::max () - average + highest_level;
|
||||
debug_assert (std::numeric_limits<std::uint64_t>::max () == average + highest_level - limiter);
|
||||
}
|
||||
else if (average < std::numeric_limits<std::uint64_t>::min () - lowest_level)
|
||||
{
|
||||
// Lowest adjusted difficulty value should be std::numeric_limits<std::uint64_t>::min ()
|
||||
limiter = std::numeric_limits<std::uint64_t>::min () - average + lowest_level;
|
||||
debug_assert (std::numeric_limits<std::uint64_t>::min () == average + lowest_level - limiter);
|
||||
}
|
||||
|
||||
// Set adjusted difficulty
|
||||
for (auto & item : elections_list)
|
||||
{
|
||||
auto existing_root (roots.get<tag_root> ().find (item.first));
|
||||
uint64_t difficulty_a = average + item.second - limiter;
|
||||
roots.get<tag_root> ().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<tag_root> ().find (item.first));
|
||||
uint64_t difficulty_a = average + item.second - limiter;
|
||||
if (existing_root->adjusted_difficulty != difficulty_a)
|
||||
{
|
||||
roots.get<tag_root> ().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<tag_root> ().end ())
|
||||
{
|
||||
root_it->election->clear_blocks ();
|
||||
root_it->election->adjust_dependent_difficulty ();
|
||||
roots.get<tag_root> ().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 ()));
|
||||
}
|
||||
|
|
|
@ -95,7 +95,8 @@ public:
|
|||
bool active (nano::qualified_root const &);
|
||||
std::shared_ptr<nano::election> election (nano::qualified_root const &) const;
|
||||
void update_difficulty (std::shared_ptr<nano::block>);
|
||||
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<std::mutex> &);
|
||||
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<nano::block_hash> adjust_difficulty_list;
|
||||
// clang-format off
|
||||
using ordered_cache = boost::multi_index_container<nano::inactive_cache_information,
|
||||
mi::indexed_by<
|
||||
|
|
|
@ -58,6 +58,7 @@ void nano::election::confirm_once (nano::election_status_type type_a)
|
|||
node_l->process_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 ());
|
||||
|
|
|
@ -78,6 +78,7 @@ public:
|
|||
bool publish (std::shared_ptr<nano::block> 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 &);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue