bench_bitcoin.cpp raw

   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 <crypto/sha256.h>
   7  #include <test/util/setup_common.h>
   8  #include <tinyformat.h>
   9  #include <util/fs.h>
  10  #include <util/time.h>
  11  
  12  #include <cstdint>
  13  #include <cstdlib>
  14  #include <exception>
  15  #include <iostream>
  16  #include <optional>
  17  #include <sstream>
  18  #include <string>
  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  }
 143