Bitcoin Core  31.0.0
P2P Digital Currency
bench_bitcoin.cpp
Go to the documentation of this file.
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);
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 {
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 
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 }
std::string SHA256AutoDetect(sha256_implementation::UseImplementation use_implementation)
Autodetect the best available SHA256 implementation.
Definition: sha256.cpp:585
void SetupCommonTestArgs(ArgsManager &argsman)
Register common test args.
fs::path GetPathArg(std::string arg, const fs::path &default_value={}) const
Return path argument or default value.
Definition: args.cpp:276
return EXIT_SUCCESS
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
disallow -nofoo syntax
Definition: args.h:115
static std::vector< double > parseAsymptote(const std::string &str)
bool ParseParameters(int argc, const char *const argv[], std::string &error)
Definition: args.cpp:177
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition: args.cpp:515
static constexpr int64_t DEFAULT_MIN_TIME_MS
void format(std::ostream &out, FormatStringCheck< sizeof...(Args)> fmt, const Args &... args)
Format list of arguments to the stream according to given format string.
Definition: tinyformat.h:1079
disable validation
Definition: args.h:110
std::string GetHelpMessage() const
Get the help string.
Definition: args.cpp:642
static const char * DEFAULT_BENCH_FILTER
static void SetupBenchArgs(ArgsManager &argsman)
static std::vector< std::string > parseTestSetupArgs(const ArgsManager &argsman)
ArgsManager & args
Definition: bitcoind.cpp:277
void AddArg(const std::string &name, const std::string &help, unsigned int flags, const OptionsCategory &cat)
Add argument.
Definition: args.cpp:589
static void RunAll(const Args &args)
Definition: bench.cpp:78
void SetupHelpOptions(ArgsManager &args)
Add help options to the args manager.
Definition: args.cpp:722
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Definition: args.h:306
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: args.cpp:461
int main(int argc, char **argv)
bool HelpRequested(const ArgsManager &args)
Definition: args.cpp:717