1 // Copyright (c) 2020-2020 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 <random.h>
8 #include <util/time.h>
9 
10 #include <optional>
11 #include <vector>
12 
13 /* A "source" is a source address from which we have received a bunch of other addresses. */
14 
15 static constexpr size_t NUM_SOURCES = 64;
16 static constexpr size_t NUM_ADDRESSES_PER_SOURCE = 256;
17 
18 static std::vector<CAddress> g_sources;
19 static std::vector<std::vector<CAddress>> g_addresses;
20 
CreateAddresses()21 static void CreateAddresses()
22 {
23     if (g_sources.size() > 0) { // already created
24         return;
25     }
26 
27     FastRandomContext rng(uint256(std::vector<unsigned char>(32, 123)));
28 
29     auto randAddr = [&rng]() {
30         in6_addr addr;
31         memcpy(&addr, rng.randbytes(sizeof(addr)).data(), sizeof(addr));
32 
33         uint16_t port;
34         memcpy(&port, rng.randbytes(sizeof(port)).data(), sizeof(port));
35         if (port == 0) {
36             port = 1;
37         }
38 
39         CAddress ret(CService(addr, port), NODE_NETWORK);
40 
41         ret.nTime = GetAdjustedTime();
42 
43         return ret;
44     };
45 
46     for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
47         g_sources.emplace_back(randAddr());
48         g_addresses.emplace_back();
49         for (size_t addr_i = 0; addr_i < NUM_ADDRESSES_PER_SOURCE; ++addr_i) {
50             g_addresses[source_i].emplace_back(randAddr());
51         }
52     }
53 }
54 
AddAddressesToAddrMan(CAddrMan & addrman)55 static void AddAddressesToAddrMan(CAddrMan& addrman)
56 {
57     for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
58         addrman.Add(g_addresses[source_i], g_sources[source_i]);
59     }
60 }
61 
FillAddrMan(CAddrMan & addrman)62 static void FillAddrMan(CAddrMan& addrman)
63 {
64     CreateAddresses();
65 
66     AddAddressesToAddrMan(addrman);
67 }
68 
69 /* Benchmarks */
70 
AddrManAdd(benchmark::Bench & bench)71 static void AddrManAdd(benchmark::Bench& bench)
72 {
73     CreateAddresses();
74 
75     CAddrMan addrman;
76 
77     bench.run([&] {
78         AddAddressesToAddrMan(addrman);
79         addrman.Clear();
80     });
81 }
82 
AddrManSelect(benchmark::Bench & bench)83 static void AddrManSelect(benchmark::Bench& bench)
84 {
85     CAddrMan addrman;
86 
87     FillAddrMan(addrman);
88 
89     bench.run([&] {
90         const auto& address = addrman.Select();
91         assert(address.GetPort() > 0);
92     });
93 }
94 
AddrManGetAddr(benchmark::Bench & bench)95 static void AddrManGetAddr(benchmark::Bench& bench)
96 {
97     CAddrMan addrman;
98 
99     FillAddrMan(addrman);
100 
101     bench.run([&] {
102         const auto& addresses = addrman.GetAddr(/* max_addresses */ 2500, /* max_pct */ 23, /* network */ std::nullopt);
103         assert(addresses.size() > 0);
104     });
105 }
106 
AddrManGood(benchmark::Bench & bench)107 static void AddrManGood(benchmark::Bench& bench)
108 {
109     /* Create many CAddrMan objects - one to be modified at each loop iteration.
110      * This is necessary because the CAddrMan::Good() method modifies the
111      * object, affecting the timing of subsequent calls to the same method and
112      * we want to do the same amount of work in every loop iteration. */
113 
114     bench.epochs(5).epochIterations(1);
115 
116     std::vector<CAddrMan> addrmans(bench.epochs() * bench.epochIterations());
117     for (auto& addrman : addrmans) {
118         FillAddrMan(addrman);
119     }
120 
121     auto markSomeAsGood = [](CAddrMan& addrman) {
122         for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
123             for (size_t addr_i = 0; addr_i < NUM_ADDRESSES_PER_SOURCE; ++addr_i) {
124                 if (addr_i % 32 == 0) {
125                     addrman.Good(g_addresses[source_i][addr_i]);
126                 }
127             }
128         }
129     };
130 
131     uint64_t i = 0;
132     bench.run([&] {
133         markSomeAsGood(addrmans.at(i));
134         ++i;
135     });
136 }
137 
138 BENCHMARK(AddrManAdd);
139 BENCHMARK(AddrManSelect);
140 BENCHMARK(AddrManGetAddr);
141 BENCHMARK(AddrManGood);
142