Skip to content

Commit 05b8c76

Browse files
committed
bench: add "priority level" to the benchmark framework
Will allow us to run certain benchmarks while skip non-prioritized ones in 'make check'.
1 parent f159378 commit 05b8c76

File tree

3 files changed

+67
-13
lines changed

3 files changed

+67
-13
lines changed

src/bench/bench.cpp

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <fs.h>
88
#include <test/util/setup_common.h>
9+
#include <util/string.h>
910

1011
#include <chrono>
1112
#include <fstream>
@@ -43,15 +44,37 @@ void GenerateTemplateResults(const std::vector<ankerl::nanobench::Result>& bench
4344

4445
namespace benchmark {
4546

47+
// map a label to one or multiple priority levels
48+
std::map<std::string, uint8_t> map_label_priority = {
49+
{"high", PriorityLevel::HIGH},
50+
{"low", PriorityLevel::LOW},
51+
{"all", 0xff}
52+
};
53+
54+
std::string ListPriorities()
55+
{
56+
using item_t = std::pair<std::string, uint8_t>;
57+
auto sort_by_priority = [](item_t a, item_t b){ return a.second < b.second; };
58+
std::set<item_t, decltype(sort_by_priority)> sorted_priorities(map_label_priority.begin(), map_label_priority.end(), sort_by_priority);
59+
return Join(sorted_priorities, ',', [](const auto& entry){ return entry.first; });
60+
}
61+
62+
uint8_t StringToPriority(const std::string& str)
63+
{
64+
auto it = map_label_priority.find(str);
65+
if (it == map_label_priority.end()) throw std::runtime_error(strprintf("Unknown priority level %s", str));
66+
return it->second;
67+
}
68+
4669
BenchRunner::BenchmarkMap& BenchRunner::benchmarks()
4770
{
48-
static std::map<std::string, BenchFunction> benchmarks_map;
71+
static BenchmarkMap benchmarks_map;
4972
return benchmarks_map;
5073
}
5174

52-
BenchRunner::BenchRunner(std::string name, BenchFunction func)
75+
BenchRunner::BenchRunner(std::string name, BenchFunction func, PriorityLevel level)
5376
{
54-
benchmarks().insert(std::make_pair(name, func));
77+
benchmarks().insert(std::make_pair(name, std::make_pair(func, level)));
5578
}
5679

5780
void BenchRunner::RunAll(const Args& args)
@@ -64,33 +87,39 @@ void BenchRunner::RunAll(const Args& args)
6487
}
6588

6689
std::vector<ankerl::nanobench::Result> benchmarkResults;
67-
for (const auto& p : benchmarks()) {
68-
if (!std::regex_match(p.first, baseMatch, reFilter)) {
90+
for (const auto& [name, bench_func] : benchmarks()) {
91+
const auto& [func, priority_level] = bench_func;
92+
93+
if (!(priority_level & args.priority)) {
94+
continue;
95+
}
96+
97+
if (!std::regex_match(name, baseMatch, reFilter)) {
6998
continue;
7099
}
71100

72101
if (args.is_list_only) {
73-
std::cout << p.first << std::endl;
102+
std::cout << name << std::endl;
74103
continue;
75104
}
76105

77106
Bench bench;
78107
if (args.sanity_check) {
79108
bench.epochs(1).epochIterations(1);
80109
}
81-
bench.name(p.first);
110+
bench.name(name);
82111
if (args.min_time > 0ms) {
83112
// convert to nanos before dividing to reduce rounding errors
84113
std::chrono::nanoseconds min_time_ns = args.min_time;
85114
bench.minEpochTime(min_time_ns / bench.epochs());
86115
}
87116

88117
if (args.asymptote.empty()) {
89-
p.second(bench);
118+
func(bench);
90119
} else {
91120
for (auto n : args.asymptote) {
92121
bench.complexityN(n);
93-
p.second(bench);
122+
func(bench);
94123
}
95124
std::cout << bench.complexityBigO() << std::endl;
96125
}

src/bench/bench.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ using ankerl::nanobench::Bench;
4141

4242
typedef std::function<void(Bench&)> BenchFunction;
4343

44+
enum PriorityLevel : uint8_t
45+
{
46+
LOW = 1 << 0,
47+
HIGH = 1 << 2,
48+
};
49+
50+
// List priority labels, comma-separated and sorted by increasing priority
51+
std::string ListPriorities();
52+
uint8_t StringToPriority(const std::string& str);
53+
4454
struct Args {
4555
bool is_list_only;
4656
bool sanity_check;
@@ -49,22 +59,24 @@ struct Args {
4959
fs::path output_csv;
5060
fs::path output_json;
5161
std::string regex_filter;
62+
uint8_t priority;
5263
};
5364

5465
class BenchRunner
5566
{
56-
typedef std::map<std::string, BenchFunction> BenchmarkMap;
67+
// maps from "name" -> (function, priority_level)
68+
typedef std::map<std::string, std::pair<BenchFunction, PriorityLevel>> BenchmarkMap;
5769
static BenchmarkMap& benchmarks();
5870

5971
public:
60-
BenchRunner(std::string name, BenchFunction func);
72+
BenchRunner(std::string name, BenchFunction func, PriorityLevel level);
6173

6274
static void RunAll(const Args& args);
6375
};
6476
} // namespace benchmark
6577

66-
// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo", foo);
78+
// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo", foo, priority_level);
6779
#define BENCHMARK(n) \
68-
benchmark::BenchRunner PASTE2(bench_, PASTE2(__LINE__, n))(STRINGIZE(n), n);
80+
benchmark::BenchRunner PASTE2(bench_, PASTE2(__LINE__, n))(STRINGIZE(n), n, benchmark::PriorityLevel::HIGH);
6981

7082
#endif // BITCOIN_BENCH_BENCH_H

src/bench/bench_bitcoin.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
static const char* DEFAULT_BENCH_FILTER = ".*";
2020
static constexpr int64_t DEFAULT_MIN_TIME_MS{10};
21+
/** Priority level default value, run "all" priority levels */
22+
static const std::string DEFAULT_PRIORITY{"all"};
2123

2224
static void SetupBenchArgs(ArgsManager& argsman)
2325
{
@@ -30,6 +32,8 @@ static void SetupBenchArgs(ArgsManager& argsman)
3032
argsman.AddArg("-output-csv=<output.csv>", "Generate CSV file with the most important benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
3133
argsman.AddArg("-output-json=<output.json>", "Generate JSON file with all benchmark results", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
3234
argsman.AddArg("-sanity-check", "Run benchmarks for only one iteration", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
35+
argsman.AddArg("-priority-level=<l1,l2,l3>", strprintf("Run benchmarks of one or multiple priority level(s) (%s), default: '%s'",
36+
benchmark::ListPriorities(), DEFAULT_PRIORITY), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
3337
}
3438

3539
// parses a comma separated list like "10,20,30,50"
@@ -45,6 +49,14 @@ static std::vector<double> parseAsymptote(const std::string& str) {
4549
return numbers;
4650
}
4751

52+
static uint8_t parsePriorityLevel(const std::string& str) {
53+
uint8_t levels{0};
54+
for (const auto& level: SplitString(str, ',')) {
55+
levels |= benchmark::StringToPriority(level);
56+
}
57+
return levels;
58+
}
59+
4860
int main(int argc, char** argv)
4961
{
5062
ArgsManager argsman;
@@ -114,6 +126,7 @@ int main(int argc, char** argv)
114126
args.output_json = argsman.GetPathArg("-output-json");
115127
args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
116128
args.sanity_check = argsman.GetBoolArg("-sanity-check", false);
129+
args.priority = parsePriorityLevel(argsman.GetArg("-priority-level", DEFAULT_PRIORITY));
117130

118131
benchmark::BenchRunner::RunAll(args);
119132

0 commit comments

Comments
 (0)