/ src / bench / bench_bitcoin.cpp
bench_bitcoin.cpp
  1  // Copyright (c) 2015-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 <bench/bench.h>
  6  
  7  #include <clientversion.h>
  8  #include <common/args.h>
  9  #include <crypto/sha256.h>
 10  #include <util/fs.h>
 11  #include <util/strencodings.h>
 12  
 13  #include <chrono>
 14  #include <cstdint>
 15  #include <iostream>
 16  #include <sstream>
 17  #include <vector>
 18  
 19  static const char* DEFAULT_BENCH_FILTER = ".*";
 20  static constexpr int64_t DEFAULT_MIN_TIME_MS{10};
 21  /** Priority level default value, run "all" priority levels */
 22  static const std::string DEFAULT_PRIORITY{"all"};
 23  
 24  static void SetupBenchArgs(ArgsManager& argsman)
 25  {
 26      SetupHelpOptions(argsman);
 27  
 28      argsman.AddArg("-asymptote=<n1,n2,n3,...>", "Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 29      argsman.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 30      argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 31      argsman.AddArg("-min-time=<milliseconds>", strprintf("Minimum runtime per benchmark, in milliseconds (default: %d)", DEFAULT_MIN_TIME_MS), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
 32      argsman.AddArg("-output-csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 33      argsman.AddArg("-output-json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 34      argsman.AddArg("-sanity-check", "Run benchmarks for only one iteration with no output", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 35      argsman.AddArg("-priority-level=<l1,l2,l3>", strprintf("Run benchmarks of one or multiple priority level(s) (%s), default: '%s'",
 36                                                             benchmark::ListPriorities(), DEFAULT_PRIORITY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
 37  }
 38  
 39  // parses a comma separated list like "10,20,30,50"
 40  static std::vector<double> parseAsymptote(const std::string& str) {
 41      std::stringstream ss(str);
 42      std::vector<double> numbers;
 43      double d;
 44      char c;
 45      while (ss >> d) {
 46          numbers.push_back(d);
 47          ss >> c;
 48      }
 49      return numbers;
 50  }
 51  
 52  static uint8_t parsePriorityLevel(const std::string& str) {
 53      uint8_t levels{0};
 54      for (const auto& level: SplitString(str, ',')) {
 55          levels |= benchmark::StringToPriority(level);
 56      }
 57      return levels;
 58  }
 59  
 60  int main(int argc, char** argv)
 61  {
 62      ArgsManager argsman;
 63      SetupBenchArgs(argsman);
 64      SHA256AutoDetect();
 65      std::string error;
 66      if (!argsman.ParseParameters(argc, argv, error)) {
 67          tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error);
 68          return EXIT_FAILURE;
 69      }
 70  
 71      if (HelpRequested(argsman)) {
 72          std::cout << "Usage:  bench_bitcoin [options]\n"
 73                       "\n"
 74                    << argsman.GetHelpMessage()
 75                    << "Description:\n"
 76                       "\n"
 77                       "  bench_bitcoin executes microbenchmarks. The quality of the benchmark results\n"
 78                       "  highly depend on the stability of the machine. It can sometimes be difficult\n"
 79                       "  to get stable, repeatable results, so here are a few tips:\n"
 80                       "\n"
 81                       "  * Use pyperf [1] to disable frequency scaling, turbo boost etc. For best\n"
 82                       "    results, use CPU pinning and CPU isolation (see [2]).\n"
 83                       "\n"
 84                       "  * Each call of run() should do exactly the same work. E.g. inserting into\n"
 85                       "    a std::vector doesn't do that as it will reallocate on certain calls. Make\n"
 86                       "    sure each run has exactly the same preconditions.\n"
 87                       "\n"
 88                       "  * If results are still not reliable, increase runtime with e.g.\n"
 89                       "    -min-time=5000 to let a benchmark run for at least 5 seconds.\n"
 90                       "\n"
 91                       "  * bench_bitcoin uses nanobench [3] for which there is extensive\n"
 92                       "    documentation available online.\n"
 93                       "\n"
 94                       "Environment Variables:\n"
 95                       "\n"
 96                       "  To attach a profiler you can run a benchmark in endless mode. This can be\n"
 97                       "  done with the environment variable NANOBENCH_ENDLESS. E.g. like so:\n"
 98                       "\n"
 99                       "    NANOBENCH_ENDLESS=MuHash ./bench_bitcoin -filter=MuHash\n"
100                       "\n"
101                       "  In rare cases it can be useful to suppress stability warnings. This can be\n"
102                       "  done with the environment variable NANOBENCH_SUPPRESS_WARNINGS, e.g:\n"
103                       "\n"
104                       "    NANOBENCH_SUPPRESS_WARNINGS=1 ./bench_bitcoin\n"
105                       "\n"
106                       "Notes:\n"
107                       "\n"
108                       "  1. pyperf\n"
109                       "     https://github.com/psf/pyperf\n"
110                       "\n"
111                       "  2. CPU pinning & isolation\n"
112                       "     https://pyperf.readthedocs.io/en/latest/system.html\n"
113                       "\n"
114                       "  3. nanobench\n"
115                       "     https://github.com/martinus/nanobench\n"
116                       "\n";
117  
118          return EXIT_SUCCESS;
119      }
120  
121      try {
122          benchmark::Args args;
123          args.asymptote = parseAsymptote(argsman.GetArg("-asymptote", ""));
124          args.is_list_only = argsman.GetBoolArg("-list", false);
125          args.min_time = std::chrono::milliseconds(argsman.GetIntArg("-min-time", DEFAULT_MIN_TIME_MS));
126          args.output_csv = argsman.GetPathArg("-output-csv");
127          args.output_json = argsman.GetPathArg("-output-json");
128          args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
129          args.sanity_check = argsman.GetBoolArg("-sanity-check", false);
130          args.priority = parsePriorityLevel(argsman.GetArg("-priority-level", DEFAULT_PRIORITY));
131  
132          benchmark::BenchRunner::RunAll(args);
133  
134          return EXIT_SUCCESS;
135      } catch (const std::exception& e) {
136          tfm::format(std::cerr, "Error: %s\n", e.what());
137          return EXIT_FAILURE;
138      }
139  }