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