Bitcoin Core  27.99.0
P2P Digital Currency
bench.cpp
Go to the documentation of this file.
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 
8 #include <util/fs.h>
9 #include <util/string.h>
10 
11 #include <chrono>
12 #include <fstream>
13 #include <functional>
14 #include <iostream>
15 #include <map>
16 #include <regex>
17 #include <string>
18 #include <vector>
19 
20 using namespace std::chrono_literals;
21 
22 const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
23 
24 const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS{};
25 
26 const std::function<std::string()> G_TEST_GET_FULL_NAME{};
27 
28 namespace {
29 
30 void GenerateTemplateResults(const std::vector<ankerl::nanobench::Result>& benchmarkResults, const fs::path& file, const char* tpl)
31 {
32  if (benchmarkResults.empty() || file.empty()) {
33  // nothing to write, bail out
34  return;
35  }
36  std::ofstream fout{file};
37  if (fout.is_open()) {
38  ankerl::nanobench::render(tpl, benchmarkResults, fout);
39  std::cout << "Created " << file << std::endl;
40  } else {
41  std::cout << "Could not write to file " << file << std::endl;
42  }
43 }
44 
45 } // namespace
46 
47 namespace benchmark {
48 
49 // map a label to one or multiple priority levels
50 std::map<std::string, uint8_t> map_label_priority = {
51  {"high", PriorityLevel::HIGH},
52  {"low", PriorityLevel::LOW},
53  {"all", 0xff}
54 };
55 
56 std::string ListPriorities()
57 {
58  using item_t = std::pair<std::string, uint8_t>;
59  auto sort_by_priority = [](item_t a, item_t b){ return a.second < b.second; };
60  std::set<item_t, decltype(sort_by_priority)> sorted_priorities(map_label_priority.begin(), map_label_priority.end(), sort_by_priority);
61  return Join(sorted_priorities, ',', [](const auto& entry){ return entry.first; });
62 }
63 
64 uint8_t StringToPriority(const std::string& str)
65 {
66  auto it = map_label_priority.find(str);
67  if (it == map_label_priority.end()) throw std::runtime_error(strprintf("Unknown priority level %s", str));
68  return it->second;
69 }
70 
71 BenchRunner::BenchmarkMap& BenchRunner::benchmarks()
72 {
73  static BenchmarkMap benchmarks_map;
74  return benchmarks_map;
75 }
76 
77 BenchRunner::BenchRunner(std::string name, BenchFunction func, PriorityLevel level)
78 {
79  benchmarks().insert(std::make_pair(name, std::make_pair(func, level)));
80 }
81 
82 void BenchRunner::RunAll(const Args& args)
83 {
84  std::regex reFilter(args.regex_filter);
85  std::smatch baseMatch;
86 
87  if (args.sanity_check) {
88  std::cout << "Running with -sanity-check option, output is being suppressed as benchmark results will be useless." << std::endl;
89  }
90 
91  std::vector<ankerl::nanobench::Result> benchmarkResults;
92  for (const auto& [name, bench_func] : benchmarks()) {
93  const auto& [func, priority_level] = bench_func;
94 
95  if (!(priority_level & args.priority)) {
96  continue;
97  }
98 
99  if (!std::regex_match(name, baseMatch, reFilter)) {
100  continue;
101  }
102 
103  if (args.is_list_only) {
104  std::cout << name << std::endl;
105  continue;
106  }
107 
108  Bench bench;
109  if (args.sanity_check) {
110  bench.epochs(1).epochIterations(1);
111  bench.output(nullptr);
112  }
113  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
const std::function< void(const std::string &)> G_TEST_LOG_FUN
This is connected to the logger.
Definition: bench.cpp:22
const std::function< std::vector< const char * >)> G_TEST_COMMAND_LINE_ARGUMENTS
Retrieve the command line arguments.
Definition: bench.cpp:24
const std::function< std::string()> G_TEST_GET_FULL_NAME
Retrieve the unit test name.
Definition: bench.cpp:26
ArgsManager & args
Definition: bitcoind.cpp:268
Main entry point to nanobench's benchmarking facility.
Definition: nanobench.h:627
ANKERL_NANOBENCH(NODISCARD) std Bench & minEpochTime(std::chrono::nanoseconds t) noexcept
Minimum time each epoch should take.
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.
Bench & epochIterations(uint64_t numIters) noexcept
Sets exactly the number of iterations for each epoch.
ANKERL_NANOBENCH(NODISCARD) std Bench & name(char const *benchmarkName)
Gets the title of the benchmark.
std::vector< BigO > complexityBigO() const
Bench & epochs(size_t numEpochs) noexcept
Controls number of epochs, the number of measurements to perform.
Bench & complexityN(T n) noexcept
Definition: nanobench.h:1265
std::map< std::string, std::pair< BenchFunction, PriorityLevel > > BenchmarkMap
Definition: bench.h:68
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:33
char const * json() noexcept
Template to generate JSON data.
void render(char const *mustacheTemplate, Bench const &bench, std::ostream &out)
Renders output from a mustache-like template and benchmark results.
std::string ListPriorities()
Definition: bench.cpp:56
std::map< std::string, uint8_t > map_label_priority
Definition: bench.cpp:50
std::function< void(Bench &)> BenchFunction
Definition: bench.h:42
PriorityLevel
Definition: bench.h:45
@ HIGH
Definition: bench.h:47
@ LOW
Definition: bench.h:46
uint8_t StringToPriority(const std::string &str)
Definition: bench.cpp:64
const char * name
Definition: rest.cpp:50
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition: string.h:68
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1162