Bitcoin Core  31.0.0
P2P Digital Currency
bench.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 
7 #include <test/util/setup_common.h> // IWYU pragma: keep
8 #include <util/check.h>
9 #include <util/fs.h>
10 
11 #include <chrono>
12 #include <compare>
13 #include <fstream>
14 #include <functional>
15 #include <iostream>
16 #include <regex>
17 #include <string>
18 #include <utility>
19 #include <vector>
20 
21 using namespace std::chrono_literals;
22 
23 const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
24 
30 static std::function<std::vector<const char*>()> g_bench_command_line_args{};
31 const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS = []() {
33 };
34 
41 static std::string g_running_benchmark_name;
42 const std::function<std::string()> G_TEST_GET_FULL_NAME = []() {
44 };
45 
46 namespace {
47 
48 void GenerateTemplateResults(const std::vector<ankerl::nanobench::Result>& benchmarkResults, const fs::path& file, const char* tpl)
49 {
50  if (benchmarkResults.empty() || file.empty()) {
51  // nothing to write, bail out
52  return;
53  }
54  std::ofstream fout{file.std_path()};
55  if (fout.is_open()) {
56  ankerl::nanobench::render(tpl, benchmarkResults, fout);
57  std::cout << "Created " << file << std::endl;
58  } else {
59  std::cout << "Could not write to file " << file << std::endl;
60  }
61 }
62 
63 } // namespace
64 
65 namespace benchmark {
66 
67 BenchRunner::BenchmarkMap& BenchRunner::benchmarks()
68 {
69  static BenchmarkMap benchmarks_map;
70  return benchmarks_map;
71 }
72 
73 BenchRunner::BenchRunner(std::string name, BenchFunction func)
74 {
75  Assert(benchmarks().try_emplace(std::move(name), std::move(func)).second);
76 }
77 
78 void BenchRunner::RunAll(const Args& args)
79 {
80  std::regex reFilter(args.regex_filter);
81  std::smatch baseMatch;
82 
83  if (args.sanity_check) {
84  std::cout << "Running with -sanity-check option, output is being suppressed as benchmark results will be useless." << std::endl;
85  }
86 
87  // Load inner test setup args
89  std::vector<const char*> ret;
90  ret.reserve(args.setup_args.size());
91  for (const auto& arg : args.setup_args) ret.emplace_back(arg.c_str());
92  return ret;
93  };
94 
95  std::vector<ankerl::nanobench::Result> benchmarkResults;
96  for (const auto& [name, func] : benchmarks()) {
97 
98  if (!std::regex_match(name, baseMatch, reFilter)) {
99  continue;
100  }
101 
102  if (args.is_list_only) {
103  std::cout << name << std::endl;
104  continue;
105  }
106 
107  Bench bench;
108  if (args.sanity_check) {
109  bench.epochs(1).epochIterations(1);
110  bench.output(nullptr);
111  }
112  bench.name(name);
114  if (args.min_time > 0ms) {
115  // convert to nanos before dividing to reduce rounding errors
116  std::chrono::nanoseconds min_time_ns = args.min_time;
117  bench.minEpochTime(min_time_ns / bench.epochs());
118  }
119 
120  if (args.asymptote.empty()) {
121  func(bench);
122  } else {
123  for (auto n : args.asymptote) {
124  bench.complexityN(n);
125  func(bench);
126  }
127  std::cout << bench.complexityBigO() << std::endl;
128  }
129 
130  if (!bench.results().empty()) {
131  benchmarkResults.push_back(bench.results().back());
132  }
133  }
134 
135  GenerateTemplateResults(benchmarkResults, args.output_csv, "# Benchmark, evals, iterations, total, min, max, median\n"
136  "{{#result}}{{name}}, {{epochs}}, {{average(iterations)}}, {{sumProduct(iterations, elapsed)}}, {{minimum(elapsed)}}, {{maximum(elapsed)}}, {{median(elapsed)}}\n"
137  "{{/result}}");
138  GenerateTemplateResults(benchmarkResults, args.output_json, ankerl::nanobench::templates::json());
139 }
140 
141 } // namespace benchmark
char const * json() noexcept
Template to generate JSON data.
int ret
Bench & epochIterations(uint64_t numIters) noexcept
Sets exactly the number of iterations for each epoch.
Bench & complexityN(T n) noexcept
Definition: nanobench.h:1265
const std::function< void(const std::string &)> G_TEST_LOG_FUN
This is connected to the logger.
Definition: bench.cpp:23
void render(char const *mustacheTemplate, Bench const &bench, std::ostream &out)
Renders output from a mustache-like template and benchmark results.
ANKERL_NANOBENCH(NODISCARD) std Bench & name(char const *benchmarkName)
Gets the title of the benchmark.
ANKERL_NANOBENCH(NODISCARD) std ANKERL_NANOBENCH(NODISCARD) std Bench & output(std::ostream *outstream) noexcept
Set the output stream where the resulting markdown table will be printed to.
std::vector< BigO > complexityBigO() const
ArgsManager & args
Definition: bitcoind.cpp:277
const char * name
Definition: rest.cpp:48
ANKERL_NANOBENCH(NODISCARD) std Bench & minEpochTime(std::chrono::nanoseconds t) noexcept
Minimum time each epoch should take.
static std::function< std::vector< const char * >)> g_bench_command_line_args
Retrieves the available test setup command line arguments that may be used in the benchmark...
Definition: bench.cpp:30
std::function< void(Bench &)> BenchFunction
Definition: bench.h:41
const std::function< std::string()> G_TEST_GET_FULL_NAME
Retrieve the unit test name.
Definition: bench.cpp:42
Bench & epochs(size_t numEpochs) noexcept
Controls number of epochs, the number of measurements to perform.
std::map< std::string, BenchFunction > BenchmarkMap
Definition: bench.h:57
Main entry point to nanobench&#39;s benchmarking facility.
Definition: nanobench.h:627
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
const std::function< std::vector< const char * >)> G_TEST_COMMAND_LINE_ARGUMENTS
Retrieve the command line arguments.
Definition: bench.cpp:31
static std::string g_running_benchmark_name
Retrieve the name of the currently in-use benchmark.
Definition: bench.cpp:41
#define Assert(val)
Identity function.
Definition: check.h:113
std::filesystem::path & std_path()
Definition: fs.h:39