Replace mruset setAddrKnown with CRollingBloomFilter addrKnown

Use a probabilistic bloom filter to keep track of which addresses
we think we have given our peers, instead of a list.

This uses much less memory, at the cost of sometimes failing to
relay an address to a peer-- worst case if the bloom filter happens
to be as full as it gets, 1-in-1,000.

Measured memory usage of a full mruset setAddrKnown: 650Kbytes
Constant memory usage of CRollingBloomFilter addrKnown: 37Kbytes.

This will also help heap fragmentation, because the 37K of storage
is allocated when a CNode is created (when a connection to a peer
is established) and then there is no per-item-remembered memory
allocation.

I plan on testing by restarting a full node with an empty peers.dat,
running a while with -debug=addrman and -debug=net, and making sure
that the 'addr' message traffic out is reasonable.
(suggestions for better tests welcome)
This commit is contained in:
Gavin Andresen 2015-04-25 16:25:44 -04:00 committed by Pieter Wuille
parent 69a5f8be0a
commit d81cff32e5
3 changed files with 9 additions and 9 deletions

View File

@ -3995,7 +3995,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
{ {
LOCK(cs_vNodes); LOCK(cs_vNodes);
// Use deterministic randomness to send to the same nodes for 24 hours // Use deterministic randomness to send to the same nodes for 24 hours
// at a time so the setAddrKnowns of the chosen nodes prevent repeats // at a time so the addrKnowns of the chosen nodes prevent repeats
static uint256 hashSalt; static uint256 hashSalt;
if (hashSalt.IsNull()) if (hashSalt.IsNull())
hashSalt = GetRandHash(); hashSalt = GetRandHash();
@ -4779,9 +4779,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
LOCK(cs_vNodes); LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes) BOOST_FOREACH(CNode* pnode, vNodes)
{ {
// Periodically clear setAddrKnown to allow refresh broadcasts // Periodically clear addrKnown to allow refresh broadcasts
if (nLastRebroadcast) if (nLastRebroadcast)
pnode->setAddrKnown.clear(); pnode->addrKnown.clear();
// Rebroadcast our address // Rebroadcast our address
AdvertizeLocal(pnode); AdvertizeLocal(pnode);
@ -4799,9 +4799,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
vAddr.reserve(pto->vAddrToSend.size()); vAddr.reserve(pto->vAddrToSend.size());
BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend) BOOST_FOREACH(const CAddress& addr, pto->vAddrToSend)
{ {
// returns true if wasn't already contained in the set if (!pto->addrKnown.contains(addr.GetKey()))
if (pto->setAddrKnown.insert(addr).second)
{ {
pto->addrKnown.insert(addr.GetKey());
vAddr.push_back(addr); vAddr.push_back(addr);
// receiver rejects addr messages larger than 1000 // receiver rejects addr messages larger than 1000
if (vAddr.size() >= 1000) if (vAddr.size() >= 1000)

View File

@ -1905,7 +1905,7 @@ bool CAddrDB::Read(CAddrMan& addr)
unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); } unsigned int ReceiveFloodSize() { return 1000*GetArg("-maxreceivebuffer", 5*1000); }
unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); } unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 1*1000); }
CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), setAddrKnown(5000) CNode::CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn, bool fInboundIn) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), addrKnown(5000, 0.001, insecure_rand())
{ {
nServices = 0; nServices = 0;
hSocket = hSocketIn; hSocket = hSocketIn;

View File

@ -300,7 +300,7 @@ public:
// flood relay // flood relay
std::vector<CAddress> vAddrToSend; std::vector<CAddress> vAddrToSend;
mruset<CAddress> setAddrKnown; CRollingBloomFilter addrKnown;
bool fGetAddr; bool fGetAddr;
std::set<uint256> setKnown; std::set<uint256> setKnown;
@ -380,7 +380,7 @@ public:
void AddAddressKnown(const CAddress& addr) void AddAddressKnown(const CAddress& addr)
{ {
setAddrKnown.insert(addr); addrKnown.insert(addr.GetKey());
} }
void PushAddress(const CAddress& addr) void PushAddress(const CAddress& addr)
@ -388,7 +388,7 @@ public:
// Known checking here is only to save space from duplicates. // Known checking here is only to save space from duplicates.
// SendMessages will filter it again for knowns that were added // SendMessages will filter it again for knowns that were added
// after addresses were pushed. // after addresses were pushed.
if (addr.IsValid() && !setAddrKnown.count(addr)) { if (addr.IsValid() && !addrKnown.contains(addr.GetKey())) {
if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) { if (vAddrToSend.size() >= MAX_ADDR_TO_SEND) {
vAddrToSend[insecure_rand() % vAddrToSend.size()] = addr; vAddrToSend[insecure_rand() % vAddrToSend.size()] = addr;
} else { } else {