Skip to content

Commit 51c69d1

Browse files
fanquakePastaPastaPasta
authored andcommitted
Merge bitcoin#18722: addrman: improve performance by using more suitable containers
a92485b addrman: use unordered_map instead of map (Vasil Dimov) Pull request description: `CAddrMan` uses `std::map` internally even though it does not require that the map's elements are sorted. `std::map`'s access time is `O(log(map size))`. `std::unordered_map` is more suitable as it has a `O(1)` access time. This patch lowers the execution times of `CAddrMan`'s methods as follows (as per `src/bench/addrman.cpp`): ``` AddrMan::Add(): -3.5% AddrMan::GetAddr(): -76% AddrMan::Good(): -0.38% AddrMan::Select(): -45% ``` ACKs for top commit: jonatack: ACK a92485b achow101: ACK a92485b hebasto: re-ACK a92485b, only suggested changes and rebased since my [previous](bitcoin#18722 (review)) review. Tree-SHA512: d82959a00e6bd68a6c4c5a265dd08849e6602ac3231293b7a3a3b7bf82ab1d3ba77f8ca682919c15c5d601b13e468b8836fcf19595248116635f7a50d02ed603
1 parent 51ff73a commit 51c69d1

File tree

3 files changed

+36
-15
lines changed

3 files changed

+36
-15
lines changed

src/addrman.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
#include <cmath>
1515
#include <optional>
16+
#include <unordered_map>
17+
#include <unordered_set>
1618

1719
int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
1820
{
@@ -115,12 +117,12 @@ CAddrInfo* CAddrMan::Find(const CService& addr, int* pnId)
115117
addr2.SetPort(0);
116118
}
117119

118-
std::map<CService, int>::iterator it = mapAddr.find(addr2);
120+
const auto it = mapAddr.find(addr2);
119121
if (it == mapAddr.end())
120122
return nullptr;
121123
if (pnId)
122124
*pnId = (*it).second;
123-
std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
125+
const auto it2 = mapInfo.find((*it).second);
124126
if (it2 != mapInfo.end())
125127
return &(*it2).second;
126128
return nullptr;
@@ -482,8 +484,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly) const
482484
int CAddrMan::Check_()
483485
{
484486
AssertLockHeld(cs);
485-
std::set<int> setTried;
486-
std::map<int, int> mapNew;
487+
std::unordered_set<int> setTried;
488+
std::unordered_map<int, int> mapNew;
487489

488490
if (vRandom.size() != (size_t)(nTried + nNew))
489491
return -7;

src/addrman.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,22 @@
88

99
#include <clientversion.h>
1010
#include <config/bitcoin-config.h>
11+
#include <fs.h>
12+
#include <hash.h>
1113
#include <netaddress.h>
1214
#include <protocol.h>
1315
#include <random.h>
16+
#include <streams.h>
1417
#include <sync.h>
1518
#include <timedata.h>
1619
#include <tinyformat.h>
1720
#include <util/system.h>
1821

19-
#include <fs.h>
20-
#include <hash.h>
2122
#include <ios>
22-
#include <map>
2323
#include <optional>
2424
#include <set>
2525
#include <stdint.h>
26-
#include <streams.h>
26+
#include <unordered_map>
2727
#include <vector>
2828

2929
/**
@@ -257,7 +257,7 @@ class CAddrMan
257257

258258
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
259259
s << nUBuckets;
260-
std::map<int, int> mapUnkIds;
260+
std::unordered_map<int, int> mapUnkIds;
261261
int nIds = 0;
262262
for (const auto& entry : mapInfo) {
263263
mapUnkIds[entry.first] = nIds;
@@ -448,13 +448,13 @@ class CAddrMan
448448

449449
// Prune new entries with refcount 0 (as a result of collisions).
450450
int nLostUnk = 0;
451-
for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) {
451+
for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) {
452452
if (it->second.fInTried == false && it->second.nRefCount == 0) {
453-
std::map<int, CAddrInfo>::const_iterator itCopy = it++;
453+
const auto itCopy = it++;
454454
Delete(itCopy->first);
455-
nLostUnk++;
455+
++nLostUnk;
456456
} else {
457-
it++;
457+
++it;
458458
}
459459
}
460460
if (nLost + nLostUnk > 0) {
@@ -682,10 +682,10 @@ class CAddrMan
682682
int nIdCount GUARDED_BY(cs);
683683

684684
//! table with information about all nIds
685-
std::map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
685+
std::unordered_map<int, CAddrInfo> mapInfo GUARDED_BY(cs);
686686

687687
//! find an nId based on its network address
688-
std::map<CService, int> mapAddr GUARDED_BY(cs);
688+
std::unordered_map<CNetAddr, int, CNetAddrHash> mapAddr GUARDED_BY(cs);
689689

690690
//! randomly-ordered vector of all nIds
691691
//! This is mutable because it is unobservable outside the class, so any

src/netaddress.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
#include <attributes.h>
1313
#include <compat.h>
14+
#include <crypto/siphash.h>
1415
#include <prevector.h>
16+
#include <random.h>
1517
#include <serialize.h>
1618
#include <tinyformat.h>
1719
#include <util/strencodings.h>
@@ -251,6 +253,7 @@ class CNetAddr
251253
}
252254
}
253255

256+
friend class CNetAddrHash;
254257
friend class CSubNet;
255258

256259
private:
@@ -465,6 +468,22 @@ class CNetAddr
465468
}
466469
};
467470

471+
class CNetAddrHash
472+
{
473+
public:
474+
size_t operator()(const CNetAddr& a) const noexcept
475+
{
476+
CSipHasher hasher(m_salt_k0, m_salt_k1);
477+
hasher.Write(a.m_net);
478+
hasher.Write(a.m_addr.data(), a.m_addr.size());
479+
return static_cast<size_t>(hasher.Finalize());
480+
}
481+
482+
private:
483+
const uint64_t m_salt_k0 = GetRand(std::numeric_limits<uint64_t>::max());
484+
const uint64_t m_salt_k1 = GetRand(std::numeric_limits<uint64_t>::max());
485+
};
486+
468487
class CSubNet
469488
{
470489
protected:

0 commit comments

Comments
 (0)