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:
Sergey Kroshnin 2020-03-04 23:56:07 +03:00 committed by GitHub
commit 1efc2e4fee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 113 additions and 78 deletions

View file

@ -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 ())
{

View file

@ -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 ());
}
}

View file

@ -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 ()));
}

View file

@ -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<

View file

@ -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 ());

View file

@ -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 &);