Currently, CAddrman::Good()
is rather slow because the process of moving an addr from new to tried involves looping over the new tables twice:
-
In
Good_()
, there is a loop searching for a new bucket the addr is currently in, but this information is never used except for aborting if it is not found anywhere (since this commit it is no longer passed toMakeTried
) This is unnecessary because in a non-corrupted addrman, an address that is not in New must be either in Tried or not at all in addrman, both cases in which we’d return early inGood_()
and never get to this point. I removed this loop (and left a check fornRefCount
as a belt-and-suspenders check). -
In
MakeTried()
, which is called fromGood_()
, another loop removes all instances of this address from new. This can be spedup by stopping the search atnRefCount==0
. Further reductions innRefCount
would only lead to an assert anyway. Moreover, the search can be started at the bucket determined by the source of the addr for whichGood
was called, so that if it is present just once in New, no further buckets need to be checked.
While calls to Good()
are not that frequent normally, the performance gain is clearly seen in the fuzz target addman_serdeser
, where, because of the slowness in creating a decently filled addrman, a shortcut was created that would directly populate the tried tables by reaching into addrman’s internals, bypassing Good()
(#21129).
I removed this workaround in the second commit: Using Good()
is still slower by a factor of 2 (down from a factor of ~60 before), but I think that this compensated by the advantages of not having to reach into the internal structures of addrman (see https://github.com/jnewbery/bitcoin/pull/18#issuecomment-775218676).
[Edit]: For benchmark results see #22974 (comment) and #22974 (comment) - the benchmark AddrManGood
shows a significant speedup by a factor >100.