Skip to content

Commit be127bc

Browse files
codablockUdjinM6
authored andcommitted
Replace vecAskFor with a priority queue (#3147)
This avoids sorting before looping through it to figure out what to request. The assumption that sorting would be cheap when vecAskFor is already mostly sorted (only unsorted at the end) turned out to be false. In reality, ~50% of CPU time was consumed by the sort when a lot of traffic (thousands of TXs) happen.
1 parent a13a918 commit be127bc

File tree

3 files changed

+24
-17
lines changed

3 files changed

+24
-17
lines changed

src/net.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3221,11 +3221,11 @@ CNode::~CNode()
32213221

32223222
void CNode::AskFor(const CInv& inv, int64_t doubleRequestDelay)
32233223
{
3224-
if (vecAskFor.size() > MAPASKFOR_MAX_SZ || setAskFor.size() > SETASKFOR_MAX_SZ) {
3224+
if (queueAskFor.size() > MAPASKFOR_MAX_SZ || setAskFor.size() > SETASKFOR_MAX_SZ) {
32253225
int64_t nNow = GetTime();
32263226
if(nNow - nLastWarningTime > WARNING_INTERVAL) {
32273227
LogPrintf("CNode::AskFor -- WARNING: inventory message dropped: vecAskFor.size = %d, setAskFor.size = %d, MAPASKFOR_MAX_SZ = %d, SETASKFOR_MAX_SZ = %d, nSkipped = %d, peer=%d\n",
3228-
vecAskFor.size(), setAskFor.size(), MAPASKFOR_MAX_SZ, SETASKFOR_MAX_SZ, nNumWarningsSkipped, id);
3228+
queueAskFor.size(), setAskFor.size(), MAPASKFOR_MAX_SZ, SETASKFOR_MAX_SZ, nNumWarningsSkipped, id);
32293229
nLastWarningTime = nNow;
32303230
nNumWarningsSkipped = 0;
32313231
}
@@ -3235,10 +3235,10 @@ void CNode::AskFor(const CInv& inv, int64_t doubleRequestDelay)
32353235
return;
32363236
}
32373237
// a peer may not have multiple non-responded queue positions for a single inv item
3238-
if (!setAskFor.insert(inv.hash).second)
3238+
if (!setAskFor.emplace(inv.hash).second)
32393239
return;
32403240

3241-
// We're using vecAskFor as a priority queue,
3241+
// We're using queueAskFor as a priority queue,
32423242
// the key is the earliest time the request can be sent
32433243
int64_t nRequestTime;
32443244
auto it = mapAlreadyAskedFor.find(inv.hash);
@@ -3262,16 +3262,17 @@ void CNode::AskFor(const CInv& inv, int64_t doubleRequestDelay)
32623262
mapAlreadyAskedFor.update(it, nRequestTime);
32633263
else
32643264
mapAlreadyAskedFor.insert(std::make_pair(inv.hash, nRequestTime));
3265-
vecAskFor.emplace_back(nRequestTime, inv);
3265+
3266+
queueAskFor.emplace(nRequestTime, inv);
3267+
setAskForInQueue.emplace(inv.hash);
32663268
}
32673269

32683270
void CNode::RemoveAskFor(const uint256& hash)
32693271
{
3270-
if (setAskFor.erase(hash)) {
3271-
vecAskFor.erase(std::remove_if(vecAskFor.begin(), vecAskFor.end(), [&](const std::pair<int64_t, CInv>& item) {
3272-
return item.second.hash == hash;
3273-
}), vecAskFor.end());
3274-
}
3272+
setAskFor.erase(hash);
3273+
// we don't really remove it from queueAskFor as it would be too expensive to rebuild the heap
3274+
// instead, we're ignoring the entry later as it won't be found in setAskForInQueue anymore
3275+
setAskForInQueue.erase(hash);
32753276
}
32763277

32773278
bool CConnman::NodeFullyConnected(const CNode* pnode)

src/net.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <memory>
3333
#include <condition_variable>
3434
#include <unordered_set>
35+
#include <queue>
3536

3637
#ifndef WIN32
3738
#include <arpa/inet.h>
@@ -843,7 +844,8 @@ class CNode
843844
std::vector<CInv> vInventoryOtherToSend;
844845
CCriticalSection cs_inventory;
845846
std::unordered_set<uint256, StaticSaltedHasher> setAskFor;
846-
std::vector<std::pair<int64_t, CInv>> vecAskFor;
847+
std::unordered_set<uint256, StaticSaltedHasher> setAskForInQueue;
848+
std::priority_queue<std::pair<int64_t, CInv>, std::vector<std::pair<int64_t, CInv>>, std::greater<>> queueAskFor;
847849
int64_t nNextInvSend;
848850
// Used for headers announcements - unfiltered blocks to relay
849851
// Also protected by cs_inventory

src/net_processing.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3964,11 +3964,15 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
39643964
//
39653965
// Message: getdata (non-blocks)
39663966
//
3967-
std::sort(pto->vecAskFor.begin(), pto->vecAskFor.end());
3968-
auto it = pto->vecAskFor.begin();
3969-
while (it != pto->vecAskFor.end() && it->first <= nNow)
3967+
while (!pto->queueAskFor.empty() && pto->queueAskFor.top().first <= nNow)
39703968
{
3971-
const CInv& inv = it->second;
3969+
const CInv& inv = pto->queueAskFor.top().second;
3970+
auto jt = pto->setAskForInQueue.find(inv.hash);
3971+
if (jt == pto->setAskForInQueue.end()) {
3972+
pto->queueAskFor.pop();
3973+
continue;
3974+
}
3975+
39723976
if (!AlreadyHave(inv))
39733977
{
39743978
LogPrint(BCLog::NET, "SendMessages -- GETDATA -- requesting inv = %s peer=%d\n", inv.ToString(), pto->GetId());
@@ -3984,9 +3988,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto, std::atomic<bool>& interruptM
39843988
LogPrint(BCLog::NET, "SendMessages -- GETDATA -- already have inv = %s peer=%d\n", inv.ToString(), pto->GetId());
39853989
pto->setAskFor.erase(inv.hash);
39863990
}
3987-
++it;
3991+
pto->queueAskFor.pop();
3992+
pto->setAskForInQueue.erase(jt);
39883993
}
3989-
pto->vecAskFor.erase(pto->vecAskFor.begin(), it);
39903994
if (!vGetData.empty()) {
39913995
connman->PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
39923996
LogPrint(BCLog::NET, "SendMessages -- GETDATA -- pushed size = %lu peer=%d\n", vGetData.size(), pto->GetId());

0 commit comments

Comments
 (0)