From a92485b2c250fd18f55d22aa32722bf52ab32bfe Mon Sep 17 00:00:00 2001 From: Vasil Dimov Date: Mon, 20 Apr 2020 17:11:08 +0200 Subject: [PATCH] addrman: use unordered_map instead of map `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% ``` --- src/addrman.cpp | 10 ++++++---- src/addrman.h | 22 +++++++++++----------- src/netaddress.h | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/src/addrman.cpp b/src/addrman.cpp index ceab1689d7e..bc5d87e5f18 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -12,6 +12,8 @@ #include #include +#include +#include int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector &asmap) const { @@ -77,12 +79,12 @@ double CAddrInfo::GetChance(int64_t nNow) const CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId) { - std::map::iterator it = mapAddr.find(addr); + const auto it = mapAddr.find(addr); if (it == mapAddr.end()) return nullptr; if (pnId) *pnId = (*it).second; - std::map::iterator it2 = mapInfo.find((*it).second); + const auto it2 = mapInfo.find((*it).second); if (it2 != mapInfo.end()) return &(*it2).second; return nullptr; @@ -408,8 +410,8 @@ CAddrInfo CAddrMan::Select_(bool newOnly) #ifdef DEBUG_ADDRMAN int CAddrMan::Check_() { - std::set setTried; - std::map mapNew; + std::unordered_set setTried; + std::unordered_map mapNew; if (vRandom.size() != (size_t)(nTried + nNew)) return -7; diff --git a/src/addrman.h b/src/addrman.h index 41994288dbd..4929fd2ecf1 100644 --- a/src/addrman.h +++ b/src/addrman.h @@ -8,22 +8,22 @@ #include #include +#include +#include #include #include #include +#include #include #include #include #include -#include -#include #include -#include #include #include #include -#include +#include #include /** @@ -251,7 +251,7 @@ public: int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30); s << nUBuckets; - std::map mapUnkIds; + std::unordered_map mapUnkIds; int nIds = 0; for (const auto& entry : mapInfo) { mapUnkIds[entry.first] = nIds; @@ -435,13 +435,13 @@ public: // Prune new entries with refcount 0 (as a result of collisions). int nLostUnk = 0; - for (std::map::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) { + for (auto it = mapInfo.cbegin(); it != mapInfo.cend(); ) { if (it->second.fInTried == false && it->second.nRefCount == 0) { - std::map::const_iterator itCopy = it++; + const auto itCopy = it++; Delete(itCopy->first); - nLostUnk++; + ++nLostUnk; } else { - it++; + ++it; } } if (nLost + nLostUnk > 0) { @@ -662,10 +662,10 @@ private: int nIdCount GUARDED_BY(cs); //! table with information about all nIds - std::map mapInfo GUARDED_BY(cs); + std::unordered_map mapInfo GUARDED_BY(cs); //! find an nId based on its network address - std::map mapAddr GUARDED_BY(cs); + std::unordered_map mapAddr GUARDED_BY(cs); //! randomly-ordered vector of all nIds std::vector vRandom GUARDED_BY(cs); diff --git a/src/netaddress.h b/src/netaddress.h index 897ce46cdaf..5c2d68150b8 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -11,7 +11,9 @@ #include #include +#include #include +#include #include #include #include @@ -254,6 +256,7 @@ class CNetAddr } } + friend class CNetAddrHash; friend class CSubNet; private: @@ -477,6 +480,22 @@ class CNetAddr } }; +class CNetAddrHash +{ +public: + size_t operator()(const CNetAddr& a) const noexcept + { + CSipHasher hasher(m_salt_k0, m_salt_k1); + hasher.Write(a.m_net); + hasher.Write(a.m_addr.data(), a.m_addr.size()); + return static_cast(hasher.Finalize()); + } + +private: + const uint64_t m_salt_k0 = GetRand(std::numeric_limits::max()); + const uint64_t m_salt_k1 = GetRand(std::numeric_limits::max()); +}; + class CSubNet { protected: