protocol: speed up sync by minimizing duplicate work

In particular, the prepare_handle_incoming_blocks call
is pretty lengthy, and entirely pointless in the common
case where several different connections will prepare
the exact same blocks.
This commit is contained in:
moneromooo-monero 2017-02-12 17:17:30 +00:00
parent 61dfa310d7
commit 8bdc86beb4
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
2 changed files with 31 additions and 2 deletions

View file

@ -135,6 +135,7 @@ namespace cryptonote
std::atomic<bool> m_synchronized; std::atomic<bool> m_synchronized;
bool m_one_request = true; bool m_one_request = true;
std::atomic<bool> m_stopping; std::atomic<bool> m_stopping;
epee::critical_section m_sync_lock;
boost::mutex m_buffer_mutex; boost::mutex m_buffer_mutex;
double get_avg_block_size(); double get_avg_block_size();

View file

@ -790,6 +790,8 @@ namespace cryptonote
context.m_remote_blockchain_height = arg.current_blockchain_height; context.m_remote_blockchain_height = arg.current_blockchain_height;
size_t count = 0; size_t count = 0;
std::vector<crypto::hash> block_hashes;
block_hashes.reserve(arg.blocks.size());
for(const block_complete_entry& block_entry: arg.blocks) for(const block_complete_entry& block_entry: arg.blocks)
{ {
if (m_stopping) if (m_stopping)
@ -807,9 +809,10 @@ namespace cryptonote
return 1; return 1;
} }
//to avoid concurrency in core between connections, suspend connections which delivered block later then first one //to avoid concurrency in core between connections, suspend connections which delivered block later then first one
const crypto::hash block_hash = get_block_hash(b);
if(count == 2) if(count == 2)
{ {
if(m_core.have_block(get_block_hash(b))) if(m_core.have_block(block_hash))
{ {
context.m_state = cryptonote_connection_context::state_idle; context.m_state = cryptonote_connection_context::state_idle;
context.m_needed_objects.clear(); context.m_needed_objects.clear();
@ -819,7 +822,7 @@ namespace cryptonote
} }
} }
auto req_it = context.m_requested_objects.find(get_block_hash(b)); auto req_it = context.m_requested_objects.find(block_hash);
if(req_it == context.m_requested_objects.end()) if(req_it == context.m_requested_objects.end())
{ {
LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block)) LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epee::string_tools::pod_to_hex(get_blob_hash(block_entry.block))
@ -836,6 +839,7 @@ namespace cryptonote
} }
context.m_requested_objects.erase(req_it); context.m_requested_objects.erase(req_it);
block_hashes.push_back(block_hash);
} }
if(context.m_requested_objects.size()) if(context.m_requested_objects.size())
@ -858,7 +862,31 @@ namespace cryptonote
uint64_t previous_height = m_core.get_current_blockchain_height(); uint64_t previous_height = m_core.get_current_blockchain_height();
// we lock all the rest to avoid having multiple connections redo a lot
// of the same work, and one of them doing it for nothing: subsequent
// connections will wait until the current one's added its blocks, then
// will add any extra it has, if any
CRITICAL_REGION_LOCAL(m_sync_lock);
// dismiss what another connection might already have done (likely everything)
uint64_t top_height;
crypto::hash top_hash;
if (m_core.get_blockchain_top(top_height, top_hash)) {
uint64_t dismiss = 1;
for (const auto &h: block_hashes) {
if (top_hash == h) {
LOG_DEBUG_CC(context, "Found current top block in synced blocks, dismissing "
<< dismiss << "/" << arg.blocks.size() << " blocks");
while (dismiss--)
arg.blocks.pop_front();
break;
}
++dismiss;
}
}
m_core.prepare_handle_incoming_blocks(arg.blocks); m_core.prepare_handle_incoming_blocks(arg.blocks);
for(const block_complete_entry& block_entry: arg.blocks) for(const block_complete_entry& block_entry: arg.blocks)
{ {
if (m_stopping) if (m_stopping)