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