addrman.cpp raw
1 // Copyright (c) 2020-present The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <addrman.h>
6 #include <bench/bench.h>
7 #include <compat/compat.h>
8 #include <netaddress.h>
9 #include <netbase.h>
10 #include <netgroup.h>
11 #include <protocol.h>
12 #include <random.h>
13 #include <uint256.h>
14 #include <util/check.h>
15 #include <util/time.h>
16
17 #include <cstring>
18 #include <optional>
19 #include <span>
20 #include <vector>
21
22 /* A "source" is a source address from which we have received a bunch of other addresses. */
23
24 static constexpr size_t NUM_SOURCES = 64;
25 static constexpr size_t NUM_ADDRESSES_PER_SOURCE = 256;
26
27 static auto EMPTY_NETGROUPMAN{NetGroupManager::NoAsmap()};
28 static constexpr uint32_t ADDRMAN_CONSISTENCY_CHECK_RATIO{0};
29
30 static std::vector<CAddress> g_sources;
31 static std::vector<std::vector<CAddress>> g_addresses;
32
33 static void CreateAddresses()
34 {
35 if (g_sources.size() > 0) { // already created
36 return;
37 }
38
39 FastRandomContext rng(uint256(std::vector<unsigned char>(32, 123)));
40
41 auto randAddr = [&rng]() {
42 in6_addr addr;
43 memcpy(&addr, rng.randbytes(sizeof(addr)).data(), sizeof(addr));
44
45 uint16_t port;
46 memcpy(&port, rng.randbytes(sizeof(port)).data(), sizeof(port));
47 if (port == 0) {
48 port = 1;
49 }
50
51 CAddress ret(CService(addr, port), NODE_NETWORK);
52
53 ret.nTime = Now<NodeSeconds>();
54
55 return ret;
56 };
57
58 for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
59 g_sources.emplace_back(randAddr());
60 g_addresses.emplace_back();
61 for (size_t addr_i = 0; addr_i < NUM_ADDRESSES_PER_SOURCE; ++addr_i) {
62 g_addresses[source_i].emplace_back(randAddr());
63 }
64 }
65 }
66
67 static void AddAddressesToAddrMan(AddrMan& addrman)
68 {
69 for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
70 addrman.Add(g_addresses[source_i], g_sources[source_i]);
71 }
72 }
73
74 static void FillAddrMan(AddrMan& addrman)
75 {
76 CreateAddresses();
77
78 AddAddressesToAddrMan(addrman);
79 }
80
81 /* Benchmarks */
82
83 static void AddrManAdd(benchmark::Bench& bench)
84 {
85 CreateAddresses();
86
87 bench.run([&] {
88 AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
89 AddAddressesToAddrMan(addrman);
90 });
91 }
92
93 static void AddrManSelect(benchmark::Bench& bench)
94 {
95 AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
96
97 FillAddrMan(addrman);
98
99 bench.run([&] {
100 const auto& address = addrman.Select();
101 assert(address.first.GetPort() > 0);
102 });
103 }
104
105 // The worst case performance of the Select() function is when there is only
106 // one address on the table, because it linearly searches every position of
107 // several buckets before identifying the correct bucket
108 static void AddrManSelectFromAlmostEmpty(benchmark::Bench& bench)
109 {
110 AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
111
112 // Add one address to the new table
113 CService addr = Lookup("250.3.1.1", 8333, false).value();
114 addrman.Add({CAddress(addr, NODE_NONE)}, addr);
115
116 bench.run([&] {
117 (void)addrman.Select();
118 });
119 }
120
121 static void AddrManSelectByNetwork(benchmark::Bench& bench)
122 {
123 AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
124
125 // add single I2P address to new table
126 CService i2p_service;
127 i2p_service.SetSpecial("udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p");
128 CAddress i2p_address(i2p_service, NODE_NONE);
129 i2p_address.nTime = Now<NodeSeconds>();
130 const CNetAddr source{LookupHost("252.2.2.2", false).value()};
131 addrman.Add({i2p_address}, source);
132
133 FillAddrMan(addrman);
134
135 bench.run([&] {
136 (void)addrman.Select(/*new_only=*/false, {NET_I2P});
137 });
138 }
139
140 static void AddrManGetAddr(benchmark::Bench& bench)
141 {
142 AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO};
143
144 FillAddrMan(addrman);
145
146 bench.run([&] {
147 const auto& addresses = addrman.GetAddr(/*max_addresses=*/2500, /*max_pct=*/23, /*network=*/std::nullopt);
148 assert(addresses.size() > 0);
149 });
150 }
151
152 static void AddrManAddThenGood(benchmark::Bench& bench)
153 {
154 auto markSomeAsGood = [](AddrMan& addrman) {
155 for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
156 for (size_t addr_i = 0; addr_i < NUM_ADDRESSES_PER_SOURCE; ++addr_i) {
157 addrman.Good(g_addresses[source_i][addr_i]);
158 }
159 }
160 };
161
162 CreateAddresses();
163
164 std::optional<AddrMan> addrman;
165 bench.setup([&] {
166 addrman.emplace(EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO);
167 AddAddressesToAddrMan(*addrman);
168 })
169 .run([&] { markSomeAsGood(*addrman); });
170 }
171
172 BENCHMARK(AddrManAdd);
173 BENCHMARK(AddrManSelect);
174 BENCHMARK(AddrManSelectFromAlmostEmpty);
175 BENCHMARK(AddrManSelectByNetwork);
176 BENCHMARK(AddrManGetAddr);
177 BENCHMARK(AddrManAddThenGood);
178