text: // Copyright (c) 2020-2020 The Picacoin Core developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include <addrman.h>
  #include <bench/bench.h>
  #include <random.h>
  #include <util/time.h>
  
  #include <vector>
  
  /* A "source" is a source address from which we have received a bunch of other addresses. */
  
  static constexpr size_t NUM_SOURCES = 64;
  static constexpr size_t NUM_ADDRESSES_PER_SOURCE = 256;
  
  static std::vector<CAddress> g_sources;
  static std::vector<std::vector<CAddress>> g_addresses;
  
  static void CreateAddresses()
  {
      if (g_sources.size() > 0) { // already created
          return;
      }
  
      FastRandomContext rng(uint256(std::vector<unsigned char>(32, 123)));
  
      auto randAddr = [&rng]() {
          in6_addr addr;
          memcpy(&addr, rng.randbytes(sizeof(addr)).data(), sizeof(addr));
  
          uint16_t port;
          memcpy(&port, rng.randbytes(sizeof(port)).data(), sizeof(port));
          if (port == 0) {
              port = 1;
          }
  
          CAddress ret(CService(addr, port), NODE_NETWORK);
  
          ret.nTime = GetAdjustedTime();
  
          return ret;
      };
  
      for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
          g_sources.emplace_back(randAddr());
          g_addresses.emplace_back();
          for (size_t addr_i = 0; addr_i < NUM_ADDRESSES_PER_SOURCE; ++addr_i) {
              g_addresses[source_i].emplace_back(randAddr());
          }
      }
  }
  
  static void AddAddressesToAddrMan(CAddrMan& addrman)
  {
      for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
          addrman.Add(g_addresses[source_i], g_sources[source_i]);
      }
  }
  
  static void FillAddrMan(CAddrMan& addrman)
  {
      CreateAddresses();
  
      AddAddressesToAddrMan(addrman);
  }
  
  /* Benchmarks */
  
  static void AddrManAdd(benchmark::Bench& bench)
  {
      CreateAddresses();
  
      CAddrMan addrman;
  
      bench.run([&] {
          AddAddressesToAddrMan(addrman);
          addrman.Clear();
      });
  }
  
  static void AddrManSelect(benchmark::Bench& bench)
  {
      CAddrMan addrman;
  
      FillAddrMan(addrman);
  
      bench.run([&] {
          const auto& address = addrman.Select();
          assert(address.GetPort() > 0);
      });
  }
  
  static void AddrManGetAddr(benchmark::Bench& bench)
  {
      CAddrMan addrman;
  
      FillAddrMan(addrman);
  
      bench.run([&] {
          const auto& addresses = addrman.GetAddr(2500, 23);
          assert(addresses.size() > 0);
      });
  }
  
  static void AddrManGood(benchmark::Bench& bench)
  {
      /* Create many CAddrMan objects - one to be modified at each loop iteration.
       * This is necessary because the CAddrMan::Good() method modifies the
       * object, affecting the timing of subsequent calls to the same method and
       * we want to do the same amount of work in every loop iteration. */
  
      bench.epochs(5).epochIterations(1);
  
      std::vector<CAddrMan> addrmans(bench.epochs() * bench.epochIterations());
      for (auto& addrman : addrmans) {
          FillAddrMan(addrman);
      }
  
      auto markSomeAsGood = [](CAddrMan& addrman) {
          for (size_t source_i = 0; source_i < NUM_SOURCES; ++source_i) {
              for (size_t addr_i = 0; addr_i < NUM_ADDRESSES_PER_SOURCE; ++addr_i) {
                  if (addr_i % 32 == 0) {
                      addrman.Good(g_addresses[source_i][addr_i]);
                  }
              }
          }
      };
  
      uint64_t i = 0;
      bench.run([&] {
          markSomeAsGood(addrmans.at(i));
          ++i;
      });
  }
  
  BENCHMARK(AddrManAdd);
  BENCHMARK(AddrManSelect);
  BENCHMARK(AddrManGetAddr);
  BENCHMARK(AddrManGood);
  