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