Merge #18011: Replace current benchmarking framework with nanobench

78c312c983 Replace current benchmarking framework with nanobench (Martin Ankerl)

Pull request description:

  Replace current benchmarking framework with nanobench

  This replaces the current benchmarking framework with nanobench [1], an
  MIT licensed single-header benchmarking library, of which I am the
  autor. This has in my opinion several advantages, especially on Linux:

  * fast: Running all benchmarks takes ~6 seconds instead of 4m13s on
    an Intel i7-8700 CPU @ 3.20GHz.

  * accurate: I ran e.g. the benchmark for SipHash_32b 10 times and
    calculate standard deviation / mean = coefficient of variation:

    * 0.57% CV for old benchmarking framework
    * 0.20% CV for nanobench

    So the benchmark results with nanobench seem to vary less than with
    the old framework.

  * It automatically determines runtime based on clock precision, no need
    to specify number of evaluations.

  * measure instructions, cycles, branches, instructions per cycle,
    branch misses (only Linux, when performance counters are available)

  * output in markdown table format.

  * Warn about unstable environment (frequency scaling, turbo, ...)

  * For better profiling, it is possible to set the environment variable
    NANOBENCH_ENDLESS to force endless running of a particular benchmark
    without the need to recompile. This makes it to e.g. run "perf top"
    and look at hotspots.

  Here is an example copy & pasted from the terminal output:

  |             ns/byte |              byte/s |    err% |        ins/byte |        cyc/byte |    IPC |       bra/byte |   miss% |     total | benchmark
  |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
  |                2.52 |      396,529,415.94 |    0.6% |           25.42 |            8.02 |  3.169 |           0.06 |    0.0% |      0.03 | `bench/crypto_hash.cpp RIPEMD160`
  |                1.87 |      535,161,444.83 |    0.3% |           21.36 |            5.95 |  3.589 |           0.06 |    0.0% |      0.02 | `bench/crypto_hash.cpp SHA1`
  |                3.22 |      310,344,174.79 |    1.1% |           36.80 |           10.22 |  3.601 |           0.09 |    0.0% |      0.04 | `bench/crypto_hash.cpp SHA256`
  |                2.01 |      496,375,796.23 |    0.0% |           18.72 |            6.43 |  2.911 |           0.01 |    1.0% |      0.00 | `bench/crypto_hash.cpp SHA256D64_1024`
  |                7.23 |      138,263,519.35 |    0.1% |           82.66 |           23.11 |  3.577 |           1.63 |    0.1% |      0.00 | `bench/crypto_hash.cpp SHA256_32b`
  |                3.04 |      328,780,166.40 |    0.3% |           35.82 |            9.69 |  3.696 |           0.03 |    0.0% |      0.03 | `bench/crypto_hash.cpp SHA512`

  [1] https://github.com/martinus/nanobench

ACKs for top commit:
  laanwj:
    ACK 78c312c983

Tree-SHA512: 9e18770b18b6f95a7d0105a4a5497d31cf4eb5efe6574f4482f6f1b4c88d7e0946b9a4a1e9e8e6ecbf41a3f2d7571240677dcb45af29a6f0584e89b25f32e49e
This commit is contained in:
Wladimir J. van der Laan 2020-07-30 15:20:19 +02:00
commit 4ebe2f6e75
No known key found for this signature in database
GPG key ID: 1E4AED62986CD25D
38 changed files with 3656 additions and 585 deletions

View file

@ -55,7 +55,7 @@ after_build:
#- 7z a bitcoin-%APPVEYOR_BUILD_VERSION%.zip %APPVEYOR_BUILD_FOLDER%\build_msvc\%platform%\%configuration%\*.exe
test_script:
- cmd: src\test_bitcoin.exe -l test_suite
- cmd: src\bench_bitcoin.exe -evals=1 -scaling=0 > NUL
- cmd: src\bench_bitcoin.exe > NUL
- ps: python test\util\bitcoin-util-test.py
- cmd: python test\util\rpcauth-test.py
# Fee estimation test failing on appveyor with: WinError 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted.

View file

@ -22,6 +22,7 @@ EXCLUDE = [
'src/reverse_iterator.h',
'src/test/fuzz/FuzzedDataProvider.h',
'src/tinyformat.h',
'src/bench/nanobench.h',
'test/functional/test_framework/bignum.py',
# python init:
'*__init__.py',

View file

@ -19,8 +19,10 @@ After compiling bitcoin-core, the benchmarks can be run with:
The output will look similar to:
```
# Benchmark, evals, iterations, total, min, max, median
AssembleBlock, 5, 700, 1.79954, 0.000510913, 0.000517018, 0.000514497
| ns/byte | byte/s | error % | benchmark
|--------------------:|--------------------:|--------:|:----------------------------------------------
| 64.13 | 15,592,356.01 | 0.1% | `Base58CheckEncode`
| 24.56 | 40,722,672.68 | 0.2% | `Base58Decode`
...
```

View file

@ -33,6 +33,8 @@ bench_bench_bitcoin_SOURCES = \
bench/merkle_root.cpp \
bench/mempool_eviction.cpp \
bench/mempool_stress.cpp \
bench/nanobench.h \
bench/nanobench.cpp \
bench/rpc_blockchain.cpp \
bench/rpc_mempool.cpp \
bench/util_time.cpp \

View file

@ -1237,8 +1237,8 @@ endif
if TARGET_WINDOWS
else
if ENABLE_BENCH
@echo "Running bench/bench_bitcoin -evals=1 -scaling=0..."
$(BENCH_BINARY) -evals=1 -scaling=0 > /dev/null
@echo "Running bench/bench_bitcoin ..."
$(BENCH_BINARY) > /dev/null
endif
endif
$(AM_V_at)$(MAKE) $(AM_MAKEFLAGS) -C secp256k1 check

View file

@ -67,52 +67,52 @@ static void FillAddrMan(CAddrMan& addrman)
/* Benchmarks */
static void AddrManAdd(benchmark::State& state)
static void AddrManAdd(benchmark::Bench& bench)
{
CreateAddresses();
CAddrMan addrman;
while (state.KeepRunning()) {
bench.run([&] {
AddAddressesToAddrMan(addrman);
addrman.Clear();
}
});
}
static void AddrManSelect(benchmark::State& state)
static void AddrManSelect(benchmark::Bench& bench)
{
CAddrMan addrman;
FillAddrMan(addrman);
while (state.KeepRunning()) {
bench.run([&] {
const auto& address = addrman.Select();
assert(address.GetPort() > 0);
}
});
}
static void AddrManGetAddr(benchmark::State& state)
static void AddrManGetAddr(benchmark::Bench& bench)
{
CAddrMan addrman;
FillAddrMan(addrman);
while (state.KeepRunning()) {
bench.run([&] {
const auto& addresses = addrman.GetAddr();
assert(addresses.size() > 0);
}
});
}
static void AddrManGood(benchmark::State& state)
static void AddrManGood(benchmark::Bench& bench)
{
/* Create many CAddrMan objects - one to be modified at each loop iteration.
* This is necessary because the CAddrMan::Good() method modifies the
* object, affecting the timing of subsequent calls to the same method and
* we want to do the same amount of work in every loop iteration. */
const uint64_t numLoops = state.m_num_iters * state.m_num_evals;
bench.epochs(5).epochIterations(1);
std::vector<CAddrMan> addrmans(numLoops);
std::vector<CAddrMan> addrmans(bench.epochs() * bench.epochIterations());
for (auto& addrman : addrmans) {
FillAddrMan(addrman);
}
@ -128,13 +128,13 @@ static void AddrManGood(benchmark::State& state)
};
uint64_t i = 0;
while (state.KeepRunning()) {
bench.run([&] {
markSomeAsGood(addrmans.at(i));
++i;
}
});
}
BENCHMARK(AddrManAdd, 5);
BENCHMARK(AddrManSelect, 1000000);
BENCHMARK(AddrManGetAddr, 500);
BENCHMARK(AddrManGood, 2);
BENCHMARK(AddrManAdd);
BENCHMARK(AddrManSelect);
BENCHMARK(AddrManGetAddr);
BENCHMARK(AddrManGood);

View file

@ -10,7 +10,7 @@
#include <vector>
static void Base58Encode(benchmark::State& state)
static void Base58Encode(benchmark::Bench& bench)
{
static const std::array<unsigned char, 32> buff = {
{
@ -19,13 +19,13 @@ static void Base58Encode(benchmark::State& state)
200, 24
}
};
while (state.KeepRunning()) {
bench.batch(buff.size()).unit("byte").run([&] {
EncodeBase58(buff.data(), buff.data() + buff.size());
}
});
}
static void Base58CheckEncode(benchmark::State& state)
static void Base58CheckEncode(benchmark::Bench& bench)
{
static const std::array<unsigned char, 32> buff = {
{
@ -36,22 +36,22 @@ static void Base58CheckEncode(benchmark::State& state)
};
std::vector<unsigned char> vch;
vch.assign(buff.begin(), buff.end());
while (state.KeepRunning()) {
bench.batch(buff.size()).unit("byte").run([&] {
EncodeBase58Check(vch);
}
});
}
static void Base58Decode(benchmark::State& state)
static void Base58Decode(benchmark::Bench& bench)
{
const char* addr = "17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem";
std::vector<unsigned char> vch;
while (state.KeepRunning()) {
bench.batch(strlen(addr)).unit("byte").run([&] {
(void) DecodeBase58(addr, vch, 64);
}
});
}
BENCHMARK(Base58Encode, 470 * 1000);
BENCHMARK(Base58CheckEncode, 320 * 1000);
BENCHMARK(Base58Decode, 800 * 1000);
BENCHMARK(Base58Encode);
BENCHMARK(Base58CheckEncode);
BENCHMARK(Base58Decode);

View file

@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
#include <bench/nanobench.h>
#include <bech32.h>
#include <util/strencodings.h>
@ -11,26 +12,26 @@
#include <vector>
static void Bech32Encode(benchmark::State& state)
static void Bech32Encode(benchmark::Bench& bench)
{
std::vector<uint8_t> v = ParseHex("c97f5a67ec381b760aeaf67573bc164845ff39a3bb26a1cee401ac67243b48db");
std::vector<unsigned char> tmp = {0};
tmp.reserve(1 + 32 * 8 / 5);
ConvertBits<8, 5, true>([&](unsigned char c) { tmp.push_back(c); }, v.begin(), v.end());
while (state.KeepRunning()) {
bench.batch(v.size()).unit("byte").run([&] {
bech32::Encode("bc", tmp);
}
});
}
static void Bech32Decode(benchmark::State& state)
static void Bech32Decode(benchmark::Bench& bench)
{
std::string addr = "bc1qkallence7tjawwvy0dwt4twc62qjgaw8f4vlhyd006d99f09";
while (state.KeepRunning()) {
bench.batch(addr.size()).unit("byte").run([&] {
bech32::Decode(addr);
}
});
}
BENCHMARK(Bech32Encode, 800 * 1000);
BENCHMARK(Bech32Decode, 800 * 1000);
BENCHMARK(Bech32Encode);
BENCHMARK(Bech32Decode);

View file

@ -8,141 +8,73 @@
#include <test/util/setup_common.h>
#include <validation.h>
#include <algorithm>
#include <assert.h>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <regex>
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
void benchmark::ConsolePrinter::header()
namespace {
void GenerateTemplateResults(const std::vector<ankerl::nanobench::Result>& benchmarkResults, const std::string& filename, const char* tpl)
{
std::cout << "# Benchmark, evals, iterations, total, min, max, median" << std::endl;
}
void benchmark::ConsolePrinter::result(const State& state)
{
auto results = state.m_elapsed_results;
std::sort(results.begin(), results.end());
double total = state.m_num_iters * std::accumulate(results.begin(), results.end(), 0.0);
double front = 0;
double back = 0;
double median = 0;
if (!results.empty()) {
front = results.front();
back = results.back();
size_t mid = results.size() / 2;
median = results[mid];
if (0 == results.size() % 2) {
median = (results[mid] + results[mid + 1]) / 2;
}
if (benchmarkResults.empty() || filename.empty()) {
// nothing to write, bail out
return;
}
std::ofstream fout(filename);
if (fout.is_open()) {
ankerl::nanobench::render(tpl, benchmarkResults, fout);
} else {
std::cout << "Could write to file '" << filename << "'" << std::endl;
}
std::cout << std::setprecision(6);
std::cout << state.m_name << ", " << state.m_num_evals << ", " << state.m_num_iters << ", " << total << ", " << front << ", " << back << ", " << median << std::endl;
}
void benchmark::ConsolePrinter::footer() {}
benchmark::PlotlyPrinter::PlotlyPrinter(std::string plotly_url, int64_t width, int64_t height)
: m_plotly_url(plotly_url), m_width(width), m_height(height)
{
}
void benchmark::PlotlyPrinter::header()
{
std::cout << "<html><head>"
<< "<script src=\"" << m_plotly_url << "\"></script>"
<< "</head><body><div id=\"myDiv\" style=\"width:" << m_width << "px; height:" << m_height << "px\"></div>"
<< "<script> var data = ["
<< std::endl;
}
void benchmark::PlotlyPrinter::result(const State& state)
{
std::cout << "{ " << std::endl
<< " name: '" << state.m_name << "', " << std::endl
<< " y: [";
const char* prefix = "";
for (const auto& e : state.m_elapsed_results) {
std::cout << prefix << std::setprecision(6) << e;
prefix = ", ";
}
std::cout << "]," << std::endl
<< " boxpoints: 'all', jitter: 0.3, pointpos: 0, type: 'box',"
<< std::endl
<< "}," << std::endl;
}
void benchmark::PlotlyPrinter::footer()
{
std::cout << "]; var layout = { showlegend: false, yaxis: { rangemode: 'tozero', autorange: true } };"
<< "Plotly.newPlot('myDiv', data, layout);"
<< "</script></body></html>";
std::cout << "Created '" << filename << "'" << std::endl;
}
} // namespace
benchmark::BenchRunner::BenchmarkMap& benchmark::BenchRunner::benchmarks()
{
static std::map<std::string, Bench> benchmarks_map;
static std::map<std::string, BenchFunction> benchmarks_map;
return benchmarks_map;
}
benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction func, uint64_t num_iters_for_one_second)
benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction func)
{
benchmarks().insert(std::make_pair(name, Bench{func, num_iters_for_one_second}));
benchmarks().insert(std::make_pair(name, func));
}
void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double scaling, const std::string& filter, bool is_list_only)
void benchmark::BenchRunner::RunAll(const Args& args)
{
if (!std::ratio_less_equal<benchmark::clock::period, std::micro>::value) {
std::cerr << "WARNING: Clock precision is worse than microsecond - benchmarks may be less accurate!\n";
}
#ifdef DEBUG
std::cerr << "WARNING: This is a debug build - may result in slower benchmarks.\n";
#endif
std::regex reFilter(filter);
std::regex reFilter(args.regex_filter);
std::smatch baseMatch;
printer.header();
std::vector<ankerl::nanobench::Result> benchmarkResults;
for (const auto& p : benchmarks()) {
if (!std::regex_match(p.first, baseMatch, reFilter)) {
continue;
}
uint64_t num_iters = static_cast<uint64_t>(p.second.num_iters_for_one_second * scaling);
if (0 == num_iters) {
num_iters = 1;
if (args.is_list_only) {
std::cout << p.first << std::endl;
continue;
}
State state(p.first, num_evals, num_iters, printer);
if (!is_list_only) {
p.second.func(state);
Bench bench;
bench.name(p.first);
if (args.asymptote.empty()) {
p.second(bench);
} else {
for (auto n : args.asymptote) {
bench.complexityN(n);
p.second(bench);
}
std::cout << bench.complexityBigO() << std::endl;
}
printer.result(state);
benchmarkResults.push_back(bench.results().back());
}
printer.footer();
}
bool benchmark::State::UpdateTimer(const benchmark::time_point current_time)
{
if (m_start_time != time_point()) {
std::chrono::duration<double> diff = current_time - m_start_time;
m_elapsed_results.push_back(diff.count() / m_num_iters);
if (m_elapsed_results.size() == m_num_evals) {
return false;
}
}
m_num_iters_left = m_num_iters - 1;
return true;
GenerateTemplateResults(benchmarkResults, args.output_csv, "# Benchmark, evals, iterations, total, min, max, median\n"
"{{#result}}{{name}}, {{epochs}}, {{average(iterations)}}, {{sumProduct(iterations, elapsed)}}, {{minimum(elapsed)}}, {{maximum(elapsed)}}, {{median(elapsed)}}\n"
"{{/result}}");
GenerateTemplateResults(benchmarkResults, args.output_json, ankerl::nanobench::templates::json());
}

View file

@ -11,131 +11,53 @@
#include <string>
#include <vector>
#include <bench/nanobench.h>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
// Simple micro-benchmarking framework; API mostly matches a subset of the Google Benchmark
// framework (see https://github.com/google/benchmark)
// Why not use the Google Benchmark framework? Because adding Yet Another Dependency
// (that uses cmake as its build system and has lots of features we don't need) isn't
// worth it.
/*
* Usage:
static void CODE_TO_TIME(benchmark::State& state)
static void CODE_TO_TIME(benchmark::Bench& bench)
{
... do any setup needed...
while (state.KeepRunning()) {
nanobench::Config().run([&] {
... do stuff you want to time...
}
});
... do any cleanup needed...
}
// default to running benchmark for 5000 iterations
BENCHMARK(CODE_TO_TIME, 5000);
BENCHMARK(CODE_TO_TIME);
*/
namespace benchmark {
// In case high_resolution_clock is steady, prefer that, otherwise use steady_clock.
struct best_clock {
using hi_res_clock = std::chrono::high_resolution_clock;
using steady_clock = std::chrono::steady_clock;
using type = std::conditional<hi_res_clock::is_steady, hi_res_clock, steady_clock>::type;
using ankerl::nanobench::Bench;
typedef std::function<void(Bench&)> BenchFunction;
struct Args {
std::string regex_filter;
bool is_list_only;
std::vector<double> asymptote;
std::string output_csv;
std::string output_json;
};
using clock = best_clock::type;
using time_point = clock::time_point;
using duration = clock::duration;
class Printer;
class State
{
public:
std::string m_name;
uint64_t m_num_iters_left;
const uint64_t m_num_iters;
const uint64_t m_num_evals;
std::vector<double> m_elapsed_results;
time_point m_start_time;
bool UpdateTimer(time_point finish_time);
State(std::string name, uint64_t num_evals, double num_iters, Printer& printer) : m_name(name), m_num_iters_left(0), m_num_iters(num_iters), m_num_evals(num_evals)
{
}
inline bool KeepRunning()
{
if (m_num_iters_left--) {
return true;
}
bool result = UpdateTimer(clock::now());
// measure again so runtime of UpdateTimer is not included
m_start_time = clock::now();
return result;
}
};
typedef std::function<void(State&)> BenchFunction;
class BenchRunner
{
struct Bench {
BenchFunction func;
uint64_t num_iters_for_one_second;
};
typedef std::map<std::string, Bench> BenchmarkMap;
typedef std::map<std::string, BenchFunction> BenchmarkMap;
static BenchmarkMap& benchmarks();
public:
BenchRunner(std::string name, BenchFunction func, uint64_t num_iters_for_one_second);
BenchRunner(std::string name, BenchFunction func);
static void RunAll(Printer& printer, uint64_t num_evals, double scaling, const std::string& filter, bool is_list_only);
};
// interface to output benchmark results.
class Printer
{
public:
virtual ~Printer() {}
virtual void header() = 0;
virtual void result(const State& state) = 0;
virtual void footer() = 0;
};
// default printer to console, shows min, max, median.
class ConsolePrinter : public Printer
{
public:
void header() override;
void result(const State& state) override;
void footer() override;
};
// creates box plot with plotly.js
class PlotlyPrinter : public Printer
{
public:
PlotlyPrinter(std::string plotly_url, int64_t width, int64_t height);
void header() override;
void result(const State& state) override;
void footer() override;
private:
std::string m_plotly_url;
int64_t m_width;
int64_t m_height;
static void RunAll(const Args& args);
};
}
// BENCHMARK(foo, num_iters_for_one_second) expands to: benchmark::BenchRunner bench_11foo("foo", num_iterations);
// Choose a num_iters_for_one_second that takes roughly 1 second. The goal is that all benchmarks should take approximately
// the same time, and scaling factor can be used that the total time is appropriate for your system.
#define BENCHMARK(n, num_iters_for_one_second) \
benchmark::BenchRunner BOOST_PP_CAT(bench_, BOOST_PP_CAT(__LINE__, n))(BOOST_PP_STRINGIZE(n), n, (num_iters_for_one_second));
// BENCHMARK(foo) expands to: benchmark::BenchRunner bench_11foo("foo");
#define BENCHMARK(n) \
benchmark::BenchRunner BOOST_PP_CAT(bench_, BOOST_PP_CAT(__LINE__, n))(BOOST_PP_STRINGIZE(n), n);
#endif // BITCOIN_BENCH_BENCH_H

View file

@ -10,26 +10,30 @@
#include <memory>
static const int64_t DEFAULT_BENCH_EVALUATIONS = 5;
static const char* DEFAULT_BENCH_FILTER = ".*";
static const char* DEFAULT_BENCH_SCALING = "1.0";
static const char* DEFAULT_BENCH_PRINTER = "console";
static const char* DEFAULT_PLOT_PLOTLYURL = "https://cdn.plot.ly/plotly-latest.min.js";
static const int64_t DEFAULT_PLOT_WIDTH = 1024;
static const int64_t DEFAULT_PLOT_HEIGHT = 768;
static void SetupBenchArgs(ArgsManager& argsman)
{
SetupHelpOptions(argsman);
argsman.AddArg("-list", "List benchmarks without executing them. Can be combined with -scaling and -filter", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-evals=<n>", strprintf("Number of measurement evaluations to perform. (default: %u)", DEFAULT_BENCH_EVALUATIONS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-list", "List benchmarks without executing them", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-scaling=<n>", strprintf("Scaling factor for benchmark's runtime (default: %u)", DEFAULT_BENCH_SCALING), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-printer=(console|plot)", strprintf("Choose printer format. console: print data to console. plot: Print results as HTML graph (default: %s)", DEFAULT_BENCH_PRINTER), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-plot-plotlyurl=<uri>", strprintf("URL to use for plotly.js (default: %s)", DEFAULT_PLOT_PLOTLYURL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-plot-width=<x>", strprintf("Plot width in pixel (default: %u)", DEFAULT_PLOT_WIDTH), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-plot-height=<x>", strprintf("Plot height in pixel (default: %u)", DEFAULT_PLOT_HEIGHT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-asymptote=n1,n2,n3,...", strprintf("Test asymptotic growth of the runtime of an algorithm, if supported by the benchmark"), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-output_csv=<output.csv>", "Generate CSV file with the most important benchmark results.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-output_json=<output.json>", "Generate JSON file with all benchmark results.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
}
// parses a comma separated list like "10,20,30,50"
static std::vector<double> parseAsymptote(const std::string& str) {
std::stringstream ss(str);
std::vector<double> numbers;
double d;
char c;
while (ss >> d) {
numbers.push_back(d);
ss >> c;
}
return numbers;
}
int main(int argc, char** argv)
@ -49,34 +53,14 @@ int main(int argc, char** argv)
return EXIT_SUCCESS;
}
int64_t evaluations = argsman.GetArg("-evals", DEFAULT_BENCH_EVALUATIONS);
std::string regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
std::string scaling_str = argsman.GetArg("-scaling", DEFAULT_BENCH_SCALING);
bool is_list_only = argsman.GetBoolArg("-list", false);
benchmark::Args args;
args.regex_filter = argsman.GetArg("-filter", DEFAULT_BENCH_FILTER);
args.is_list_only = argsman.GetBoolArg("-list", false);
args.asymptote = parseAsymptote(argsman.GetArg("-asymptote", ""));
args.output_csv = argsman.GetArg("-output_csv", "");
args.output_json = argsman.GetArg("-output_json", "");
if (evaluations == 0) {
return EXIT_SUCCESS;
} else if (evaluations < 0) {
tfm::format(std::cerr, "Error parsing evaluations argument: %d\n", evaluations);
return EXIT_FAILURE;
}
double scaling_factor;
if (!ParseDouble(scaling_str, &scaling_factor)) {
tfm::format(std::cerr, "Error parsing scaling factor as double: %s\n", scaling_str);
return EXIT_FAILURE;
}
std::unique_ptr<benchmark::Printer> printer = MakeUnique<benchmark::ConsolePrinter>();
std::string printer_arg = argsman.GetArg("-printer", DEFAULT_BENCH_PRINTER);
if ("plot" == printer_arg) {
printer.reset(new benchmark::PlotlyPrinter(
argsman.GetArg("-plot-plotlyurl", DEFAULT_PLOT_PLOTLYURL),
argsman.GetArg("-plot-width", DEFAULT_PLOT_WIDTH),
argsman.GetArg("-plot-height", DEFAULT_PLOT_HEIGHT)));
}
benchmark::BenchRunner::RunAll(*printer, evaluations, scaling_factor, regex_filter, is_list_only);
benchmark::BenchRunner::RunAll(args);
return EXIT_SUCCESS;
}

View file

@ -14,7 +14,7 @@
#include <vector>
static void AssembleBlock(benchmark::State& state)
static void AssembleBlock(benchmark::Bench& bench)
{
TestingSetup test_setup{
CBaseChainParams::REGTEST,
@ -54,9 +54,9 @@ static void AssembleBlock(benchmark::State& state)
}
}
while (state.KeepRunning()) {
bench.run([&] {
PrepareBlock(test_setup.m_node, SCRIPT_PUB);
}
});
}
BENCHMARK(AssembleBlock, 700);
BENCHMARK(AssembleBlock);

View file

@ -16,7 +16,7 @@
// characteristics than e.g. reindex timings. But that's not a requirement of
// every benchmark."
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
static void CCoinsCaching(benchmark::State& state)
static void CCoinsCaching(benchmark::Bench& bench)
{
const ECCVerifyHandle verify_handle;
ECC_Start();
@ -44,11 +44,11 @@ static void CCoinsCaching(benchmark::State& state)
// Benchmark.
const CTransaction tx_1(t1);
while (state.KeepRunning()) {
bench.run([&] {
bool success = AreInputsStandard(tx_1, coins);
assert(success);
}
});
ECC_Stop();
}
BENCHMARK(CCoinsCaching, 170 * 1000);
BENCHMARK(CCoinsCaching);

View file

@ -11,7 +11,7 @@ static const uint64_t BUFFER_SIZE_TINY = 64;
static const uint64_t BUFFER_SIZE_SMALL = 256;
static const uint64_t BUFFER_SIZE_LARGE = 1024*1024;
static void CHACHA20(benchmark::State& state, size_t buffersize)
static void CHACHA20(benchmark::Bench& bench, size_t buffersize)
{
std::vector<uint8_t> key(32,0);
ChaCha20 ctx(key.data(), key.size());
@ -19,26 +19,26 @@ static void CHACHA20(benchmark::State& state, size_t buffersize)
ctx.Seek(0);
std::vector<uint8_t> in(buffersize,0);
std::vector<uint8_t> out(buffersize,0);
while (state.KeepRunning()) {
bench.batch(in.size()).unit("byte").run([&] {
ctx.Crypt(in.data(), out.data(), in.size());
}
});
}
static void CHACHA20_64BYTES(benchmark::State& state)
static void CHACHA20_64BYTES(benchmark::Bench& bench)
{
CHACHA20(state, BUFFER_SIZE_TINY);
CHACHA20(bench, BUFFER_SIZE_TINY);
}
static void CHACHA20_256BYTES(benchmark::State& state)
static void CHACHA20_256BYTES(benchmark::Bench& bench)
{
CHACHA20(state, BUFFER_SIZE_SMALL);
CHACHA20(bench, BUFFER_SIZE_SMALL);
}
static void CHACHA20_1MB(benchmark::State& state)
static void CHACHA20_1MB(benchmark::Bench& bench)
{
CHACHA20(state, BUFFER_SIZE_LARGE);
CHACHA20(bench, BUFFER_SIZE_LARGE);
}
BENCHMARK(CHACHA20_64BYTES, 500000);
BENCHMARK(CHACHA20_256BYTES, 250000);
BENCHMARK(CHACHA20_1MB, 340);
BENCHMARK(CHACHA20_64BYTES);
BENCHMARK(CHACHA20_256BYTES);
BENCHMARK(CHACHA20_1MB);

View file

@ -21,7 +21,7 @@ static const unsigned char k2[32] = {0};
static ChaCha20Poly1305AEAD aead(k1, 32, k2, 32);
static void CHACHA20_POLY1305_AEAD(benchmark::State& state, size_t buffersize, bool include_decryption)
static void CHACHA20_POLY1305_AEAD(benchmark::Bench& bench, size_t buffersize, bool include_decryption)
{
std::vector<unsigned char> in(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
std::vector<unsigned char> out(buffersize + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0);
@ -29,7 +29,7 @@ static void CHACHA20_POLY1305_AEAD(benchmark::State& state, size_t buffersize, b
uint64_t seqnr_aad = 0;
int aad_pos = 0;
uint32_t len = 0;
while (state.KeepRunning()) {
bench.batch(buffersize).unit("byte").run([&] {
// encrypt or decrypt the buffer with a static key
assert(aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffersize, true));
@ -53,70 +53,71 @@ static void CHACHA20_POLY1305_AEAD(benchmark::State& state, size_t buffersize, b
seqnr_aad = 0;
aad_pos = 0;
}
}
});
}
static void CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT(benchmark::State& state)
static void CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT(benchmark::Bench& bench)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_TINY, false);
CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_TINY, false);
}
static void CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT(benchmark::State& state)
static void CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT(benchmark::Bench& bench)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_SMALL, false);
CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_SMALL, false);
}
static void CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT(benchmark::State& state)
static void CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT(benchmark::Bench& bench)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_LARGE, false);
CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_LARGE, false);
}
static void CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT(benchmark::State& state)
static void CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT(benchmark::Bench& bench)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_TINY, true);
CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_TINY, true);
}
static void CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT(benchmark::State& state)
static void CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT(benchmark::Bench& bench)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_SMALL, true);
CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_SMALL, true);
}
static void CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT(benchmark::State& state)
static void CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT(benchmark::Bench& bench)
{
CHACHA20_POLY1305_AEAD(state, BUFFER_SIZE_LARGE, true);
CHACHA20_POLY1305_AEAD(bench, BUFFER_SIZE_LARGE, true);
}
// Add Hash() (dbl-sha256) bench for comparison
static void HASH(benchmark::State& state, size_t buffersize)
static void HASH(benchmark::Bench& bench, size_t buffersize)
{
uint8_t hash[CHash256::OUTPUT_SIZE];
std::vector<uint8_t> in(buffersize,0);
while (state.KeepRunning())
bench.batch(in.size()).unit("byte").run([&] {
CHash256().Write(in.data(), in.size()).Finalize(hash);
});
}
static void HASH_64BYTES(benchmark::State& state)
static void HASH_64BYTES(benchmark::Bench& bench)
{
HASH(state, BUFFER_SIZE_TINY);
HASH(bench, BUFFER_SIZE_TINY);
}
static void HASH_256BYTES(benchmark::State& state)
static void HASH_256BYTES(benchmark::Bench& bench)
{
HASH(state, BUFFER_SIZE_SMALL);
HASH(bench, BUFFER_SIZE_SMALL);
}
static void HASH_1MB(benchmark::State& state)
static void HASH_1MB(benchmark::Bench& bench)
{
HASH(state, BUFFER_SIZE_LARGE);
HASH(bench, BUFFER_SIZE_LARGE);
}
BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT, 500000);
BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT, 250000);
BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT, 340);
BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT, 500000);
BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT, 250000);
BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT, 340);
BENCHMARK(HASH_64BYTES, 500000);
BENCHMARK(HASH_256BYTES, 250000);
BENCHMARK(HASH_1MB, 340);
BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ONLY_ENCRYPT);
BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ONLY_ENCRYPT);
BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ONLY_ENCRYPT);
BENCHMARK(CHACHA20_POLY1305_AEAD_64BYTES_ENCRYPT_DECRYPT);
BENCHMARK(CHACHA20_POLY1305_AEAD_256BYTES_ENCRYPT_DECRYPT);
BENCHMARK(CHACHA20_POLY1305_AEAD_1MB_ENCRYPT_DECRYPT);
BENCHMARK(HASH_64BYTES);
BENCHMARK(HASH_256BYTES);
BENCHMARK(HASH_1MB);

View file

@ -14,21 +14,21 @@
// a block off the wire, but before we can relay the block on to peers using
// compact block relay.
static void DeserializeBlockTest(benchmark::State& state)
static void DeserializeBlockTest(benchmark::Bench& bench)
{
CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
char a = '\0';
stream.write(&a, 1); // Prevent compaction
while (state.KeepRunning()) {
bench.unit("block").run([&] {
CBlock block;
stream >> block;
bool rewound = stream.Rewind(benchmark::data::block413567.size());
assert(rewound);
}
});
}
static void DeserializeAndCheckBlockTest(benchmark::State& state)
static void DeserializeAndCheckBlockTest(benchmark::Bench& bench)
{
CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
char a = '\0';
@ -36,7 +36,7 @@ static void DeserializeAndCheckBlockTest(benchmark::State& state)
const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
while (state.KeepRunning()) {
bench.unit("block").run([&] {
CBlock block; // Note that CBlock caches its checked state, so we need to recreate it here
stream >> block;
bool rewound = stream.Rewind(benchmark::data::block413567.size());
@ -45,8 +45,8 @@ static void DeserializeAndCheckBlockTest(benchmark::State& state)
BlockValidationState validationState;
bool checked = CheckBlock(block, validationState, chainParams->GetConsensus());
assert(checked);
}
});
}
BENCHMARK(DeserializeBlockTest, 130);
BENCHMARK(DeserializeAndCheckBlockTest, 160);
BENCHMARK(DeserializeBlockTest);
BENCHMARK(DeserializeAndCheckBlockTest);

View file

@ -24,7 +24,7 @@ static const unsigned int QUEUE_BATCH_SIZE = 128;
// This Benchmark tests the CheckQueue with a slightly realistic workload,
// where checks all contain a prevector that is indirect 50% of the time
// and there is a little bit of work done between calls to Add.
static void CCheckQueueSpeedPrevectorJob(benchmark::State& state)
static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
{
const ECCVerifyHandle verify_handle;
ECC_Start();
@ -47,23 +47,28 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::State& state)
for (auto x = 0; x < std::max(MIN_CORES, GetNumCores()); ++x) {
tg.create_thread([&]{queue.Thread();});
}
while (state.KeepRunning()) {
// create all the data once, then submit copies in the benchmark.
FastRandomContext insecure_rand(true);
std::vector<std::vector<PrevectorJob>> vBatches(BATCHES);
for (auto& vChecks : vBatches) {
vChecks.reserve(BATCH_SIZE);
for (size_t x = 0; x < BATCH_SIZE; ++x)
vChecks.emplace_back(insecure_rand);
}
bench.minEpochIterations(10).batch(BATCH_SIZE * BATCHES).unit("job").run([&] {
// Make insecure_rand here so that each iteration is identical.
FastRandomContext insecure_rand(true);
CCheckQueueControl<PrevectorJob> control(&queue);
std::vector<std::vector<PrevectorJob>> vBatches(BATCHES);
for (auto& vChecks : vBatches) {
vChecks.reserve(BATCH_SIZE);
for (size_t x = 0; x < BATCH_SIZE; ++x)
vChecks.emplace_back(insecure_rand);
for (auto vChecks : vBatches) {
control.Add(vChecks);
}
// control waits for completion by RAII, but
// it is done explicitly here for clarity
control.Wait();
}
});
tg.interrupt_all();
tg.join_all();
ECC_Stop();
}
BENCHMARK(CCheckQueueSpeedPrevectorJob, 1400);
BENCHMARK(CCheckQueueSpeedPrevectorJob);

View file

@ -27,7 +27,7 @@ static void addCoin(const CAmount& nValue, const CWallet& wallet, std::vector<st
// same one over and over isn't too useful. Generating random isn't useful
// either for measurements."
// (https://github.com/bitcoin/bitcoin/issues/7883#issuecomment-224807484)
static void CoinSelection(benchmark::State& state)
static void CoinSelection(benchmark::Bench& bench)
{
NodeContext node;
auto chain = interfaces::MakeChain(node);
@ -51,7 +51,7 @@ static void CoinSelection(benchmark::State& state)
const CoinEligibilityFilter filter_standard(1, 6, 0);
const CoinSelectionParams coin_selection_params(true, 34, 148, CFeeRate(0), 0);
while (state.KeepRunning()) {
bench.run([&] {
std::set<CInputCoin> setCoinsRet;
CAmount nValueRet;
bool bnb_used;
@ -59,7 +59,7 @@ static void CoinSelection(benchmark::State& state)
assert(success);
assert(nValueRet == 1003 * COIN);
assert(setCoinsRet.size() == 2);
}
});
}
typedef std::set<CInputCoin> CoinSet;
@ -91,7 +91,7 @@ static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
return target;
}
static void BnBExhaustion(benchmark::State& state)
static void BnBExhaustion(benchmark::Bench& bench)
{
// Setup
testWallet.SetupLegacyScriptPubKeyMan();
@ -100,7 +100,7 @@ static void BnBExhaustion(benchmark::State& state)
CAmount value_ret = 0;
CAmount not_input_fees = 0;
while (state.KeepRunning()) {
bench.run([&] {
// Benchmark
CAmount target = make_hard_case(17, utxo_pool);
SelectCoinsBnB(utxo_pool, target, 0, selection, value_ret, not_input_fees); // Should exhaust
@ -108,8 +108,8 @@ static void BnBExhaustion(benchmark::State& state)
// Cleanup
utxo_pool.clear();
selection.clear();
}
});
}
BENCHMARK(CoinSelection, 650);
BENCHMARK(BnBExhaustion, 650);
BENCHMARK(CoinSelection);
BENCHMARK(BnBExhaustion);

View file

@ -16,88 +16,92 @@
/* Number of bytes to hash per iteration */
static const uint64_t BUFFER_SIZE = 1000*1000;
static void RIPEMD160(benchmark::State& state)
static void RIPEMD160(benchmark::Bench& bench)
{
uint8_t hash[CRIPEMD160::OUTPUT_SIZE];
std::vector<uint8_t> in(BUFFER_SIZE,0);
while (state.KeepRunning())
bench.batch(in.size()).unit("byte").run([&] {
CRIPEMD160().Write(in.data(), in.size()).Finalize(hash);
});
}
static void SHA1(benchmark::State& state)
static void SHA1(benchmark::Bench& bench)
{
uint8_t hash[CSHA1::OUTPUT_SIZE];
std::vector<uint8_t> in(BUFFER_SIZE,0);
while (state.KeepRunning())
bench.batch(in.size()).unit("byte").run([&] {
CSHA1().Write(in.data(), in.size()).Finalize(hash);
});
}
static void SHA256(benchmark::State& state)
static void SHA256(benchmark::Bench& bench)
{
uint8_t hash[CSHA256::OUTPUT_SIZE];
std::vector<uint8_t> in(BUFFER_SIZE,0);
while (state.KeepRunning())
bench.batch(in.size()).unit("byte").run([&] {
CSHA256().Write(in.data(), in.size()).Finalize(hash);
});
}
static void SHA256_32b(benchmark::State& state)
static void SHA256_32b(benchmark::Bench& bench)
{
std::vector<uint8_t> in(32,0);
while (state.KeepRunning()) {
bench.batch(in.size()).unit("byte").run([&] {
CSHA256()
.Write(in.data(), in.size())
.Finalize(in.data());
}
});
}
static void SHA256D64_1024(benchmark::State& state)
static void SHA256D64_1024(benchmark::Bench& bench)
{
std::vector<uint8_t> in(64 * 1024, 0);
while (state.KeepRunning()) {
bench.batch(in.size()).unit("byte").run([&] {
SHA256D64(in.data(), in.data(), 1024);
}
});
}
static void SHA512(benchmark::State& state)
static void SHA512(benchmark::Bench& bench)
{
uint8_t hash[CSHA512::OUTPUT_SIZE];
std::vector<uint8_t> in(BUFFER_SIZE,0);
while (state.KeepRunning())
bench.batch(in.size()).unit("byte").run([&] {
CSHA512().Write(in.data(), in.size()).Finalize(hash);
});
}
static void SipHash_32b(benchmark::State& state)
static void SipHash_32b(benchmark::Bench& bench)
{
uint256 x;
uint64_t k1 = 0;
while (state.KeepRunning()) {
bench.run([&] {
*((uint64_t*)x.begin()) = SipHashUint256(0, ++k1, x);
}
});
}
static void FastRandom_32bit(benchmark::State& state)
static void FastRandom_32bit(benchmark::Bench& bench)
{
FastRandomContext rng(true);
while (state.KeepRunning()) {
bench.run([&] {
rng.rand32();
}
});
}
static void FastRandom_1bit(benchmark::State& state)
static void FastRandom_1bit(benchmark::Bench& bench)
{
FastRandomContext rng(true);
while (state.KeepRunning()) {
bench.run([&] {
rng.randbool();
}
});
}
BENCHMARK(RIPEMD160, 440);
BENCHMARK(SHA1, 570);
BENCHMARK(SHA256, 340);
BENCHMARK(SHA512, 330);
BENCHMARK(RIPEMD160);
BENCHMARK(SHA1);
BENCHMARK(SHA256);
BENCHMARK(SHA512);
BENCHMARK(SHA256_32b, 4700 * 1000);
BENCHMARK(SipHash_32b, 40 * 1000 * 1000);
BENCHMARK(SHA256D64_1024, 7400);
BENCHMARK(FastRandom_32bit, 110 * 1000 * 1000);
BENCHMARK(FastRandom_1bit, 440 * 1000 * 1000);
BENCHMARK(SHA256_32b);
BENCHMARK(SipHash_32b);
BENCHMARK(SHA256D64_1024);
BENCHMARK(FastRandom_32bit);
BENCHMARK(FastRandom_1bit);

View file

@ -12,7 +12,7 @@
#include <validation.h>
static void DuplicateInputs(benchmark::State& state)
static void DuplicateInputs(benchmark::Bench& bench)
{
TestingSetup test_setup{
CBaseChainParams::REGTEST,
@ -61,11 +61,11 @@ static void DuplicateInputs(benchmark::State& state)
block.hashMerkleRoot = BlockMerkleRoot(block);
while (state.KeepRunning()) {
bench.run([&] {
BlockValidationState cvstate{};
assert(!CheckBlock(block, cvstate, chainparams.GetConsensus(), false, false));
assert(cvstate.GetRejectReason() == "bad-txns-inputs-duplicate");
}
});
}
BENCHMARK(DuplicateInputs, 10);
BENCHMARK(DuplicateInputs);

View file

@ -3,31 +3,19 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
#include <util/time.h>
// Sanity test: this should loop ten times, and
// min/max/average should be close to 100ms.
static void Sleep100ms(benchmark::State& state)
{
while (state.KeepRunning()) {
UninterruptibleSleep(std::chrono::milliseconds{100});
}
}
BENCHMARK(Sleep100ms, 10);
// Extremely fast-running benchmark:
#include <math.h>
volatile double sum = 0.0; // volatile, global so not optimized away
static void Trig(benchmark::State& state)
static void Trig(benchmark::Bench& bench)
{
double d = 0.01;
while (state.KeepRunning()) {
bench.run([&] {
sum += sin(d);
d += 0.000001;
}
});
}
BENCHMARK(Trig, 12 * 1000 * 1000);
BENCHMARK(Trig);

View file

@ -5,7 +5,7 @@
#include <bench/bench.h>
#include <blockfilter.h>
static void ConstructGCSFilter(benchmark::State& state)
static void ConstructGCSFilter(benchmark::Bench& bench)
{
GCSFilter::ElementSet elements;
for (int i = 0; i < 10000; ++i) {
@ -16,14 +16,14 @@ static void ConstructGCSFilter(benchmark::State& state)
}
uint64_t siphash_k0 = 0;
while (state.KeepRunning()) {
bench.batch(elements.size()).unit("elem").run([&] {
GCSFilter filter({siphash_k0, 0, 20, 1 << 20}, elements);
siphash_k0++;
}
});
}
static void MatchGCSFilter(benchmark::State& state)
static void MatchGCSFilter(benchmark::Bench& bench)
{
GCSFilter::ElementSet elements;
for (int i = 0; i < 10000; ++i) {
@ -34,10 +34,10 @@ static void MatchGCSFilter(benchmark::State& state)
}
GCSFilter filter({0, 0, 20, 1 << 20}, elements);
while (state.KeepRunning()) {
bench.unit("elem").run([&] {
filter.Match(GCSFilter::Element());
}
});
}
BENCHMARK(ConstructGCSFilter, 1000);
BENCHMARK(MatchGCSFilter, 50 * 1000);
BENCHMARK(ConstructGCSFilter);
BENCHMARK(MatchGCSFilter);

View file

@ -8,7 +8,7 @@
#include <uint256.h>
static void PrePadded(benchmark::State& state)
static void PrePadded(benchmark::Bench& bench)
{
CSHA256 hasher;
@ -18,30 +18,30 @@ static void PrePadded(benchmark::State& state)
hasher.Write(nonce.begin(), 32);
hasher.Write(nonce.begin(), 32);
uint256 data = GetRandHash();
while (state.KeepRunning()) {
bench.run([&] {
unsigned char out[32];
CSHA256 h = hasher;
h.Write(data.begin(), 32);
h.Finalize(out);
}
});
}
BENCHMARK(PrePadded, 10000);
BENCHMARK(PrePadded);
static void RegularPadded(benchmark::State& state)
static void RegularPadded(benchmark::Bench& bench)
{
CSHA256 hasher;
// Setup the salted hasher
uint256 nonce = GetRandHash();
uint256 data = GetRandHash();
while (state.KeepRunning()) {
bench.run([&] {
unsigned char out[32];
CSHA256 h = hasher;
h.Write(nonce.begin(), 32);
h.Write(data.begin(), 32);
h.Finalize(out);
}
});
}
BENCHMARK(RegularPadded, 10000);
BENCHMARK(RegularPadded);

View file

@ -9,10 +9,9 @@
#include <vector>
#define ASIZE 2048
#define BITER 5000
#define MSIZE 2048
static void BenchLockedPool(benchmark::State& state)
static void BenchLockedPool(benchmark::Bench& bench)
{
void *synth_base = reinterpret_cast<void*>(0x08000000);
const size_t synth_size = 1024*1024;
@ -22,24 +21,22 @@ static void BenchLockedPool(benchmark::State& state)
for (int x=0; x<ASIZE; ++x)
addr.push_back(nullptr);
uint32_t s = 0x12345678;
while (state.KeepRunning()) {
for (int x=0; x<BITER; ++x) {
int idx = s & (addr.size()-1);
if (s & 0x80000000) {
b.free(addr[idx]);
addr[idx] = nullptr;
} else if(!addr[idx]) {
addr[idx] = b.alloc((s >> 16) & (MSIZE-1));
}
bool lsb = s & 1;
s >>= 1;
if (lsb)
s ^= 0xf00f00f0; // LFSR period 0xf7ffffe0
bench.run([&] {
int idx = s & (addr.size() - 1);
if (s & 0x80000000) {
b.free(addr[idx]);
addr[idx] = nullptr;
} else if (!addr[idx]) {
addr[idx] = b.alloc((s >> 16) & (MSIZE - 1));
}
}
bool lsb = s & 1;
s >>= 1;
if (lsb)
s ^= 0xf00f00f0; // LFSR period 0xf7ffffe0
});
for (void *ptr: addr)
b.free(ptr);
addr.clear();
}
BENCHMARK(BenchLockedPool, 1300);
BENCHMARK(BenchLockedPool);

View file

@ -23,7 +23,7 @@ static void AddTx(const CTransactionRef& tx, const CAmount& nFee, CTxMemPool& po
// Right now this is only testing eviction performance in an extremely small
// mempool. Code needs to be written to generate a much wider variety of
// unique transactions for a more meaningful performance measurement.
static void MempoolEviction(benchmark::State& state)
static void MempoolEviction(benchmark::Bench& bench)
{
TestingSetup test_setup{
CBaseChainParams::REGTEST,
@ -125,7 +125,7 @@ static void MempoolEviction(benchmark::State& state)
const CTransactionRef tx6_r{MakeTransactionRef(tx6)};
const CTransactionRef tx7_r{MakeTransactionRef(tx7)};
while (state.KeepRunning()) {
bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
AddTx(tx1_r, 10000LL, pool);
AddTx(tx2_r, 5000LL, pool);
AddTx(tx3_r, 20000LL, pool);
@ -135,7 +135,7 @@ static void MempoolEviction(benchmark::State& state)
AddTx(tx7_r, 9000LL, pool);
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4);
pool.TrimToSize(GetVirtualTransactionSize(*tx1_r));
}
});
}
BENCHMARK(MempoolEviction, 41000);
BENCHMARK(MempoolEviction);

View file

@ -26,8 +26,13 @@ struct Available {
Available(CTransactionRef& ref, size_t tx_count) : ref(ref), tx_count(tx_count){}
};
static void ComplexMemPool(benchmark::State& state)
static void ComplexMemPool(benchmark::Bench& bench)
{
int childTxs = 800;
if (bench.complexityN() > 1) {
childTxs = static_cast<int>(bench.complexityN());
}
FastRandomContext det_rand{true};
std::vector<Available> available_coins;
std::vector<CTransactionRef> ordered_coins;
@ -46,7 +51,7 @@ static void ComplexMemPool(benchmark::State& state)
ordered_coins.emplace_back(MakeTransactionRef(tx));
available_coins.emplace_back(ordered_coins.back(), tx_counter++);
}
for (auto x = 0; x < 800 && !available_coins.empty(); ++x) {
for (auto x = 0; x < childTxs && !available_coins.empty(); ++x) {
CMutableTransaction tx = CMutableTransaction();
size_t n_ancestors = det_rand.randrange(10)+1;
for (size_t ancestor = 0; ancestor < n_ancestors && !available_coins.empty(); ++ancestor){
@ -77,13 +82,13 @@ static void ComplexMemPool(benchmark::State& state)
TestingSetup test_setup;
CTxMemPool pool;
LOCK2(cs_main, pool.cs);
while (state.KeepRunning()) {
bench.run([&]() NO_THREAD_SAFETY_ANALYSIS {
for (auto& tx : ordered_coins) {
AddTx(tx, pool);
}
pool.TrimToSize(pool.DynamicMemoryUsage() * 3 / 4);
pool.TrimToSize(GetVirtualTransactionSize(*ordered_coins.front()));
}
});
}
BENCHMARK(ComplexMemPool, 1);
BENCHMARK(ComplexMemPool);

View file

@ -8,7 +8,7 @@
#include <random.h>
#include <uint256.h>
static void MerkleRoot(benchmark::State& state)
static void MerkleRoot(benchmark::Bench& bench)
{
FastRandomContext rng(true);
std::vector<uint256> leaves;
@ -16,11 +16,11 @@ static void MerkleRoot(benchmark::State& state)
for (auto& item : leaves) {
item = rng.rand256();
}
while (state.KeepRunning()) {
bench.batch(leaves.size()).unit("leaf").run([&] {
bool mutation = false;
uint256 hash = ComputeMerkleRoot(std::vector<uint256>(leaves), &mutation);
leaves[mutation] = hash;
}
});
}
BENCHMARK(MerkleRoot, 800);
BENCHMARK(MerkleRoot);

6
src/bench/nanobench.cpp Normal file
View file

@ -0,0 +1,6 @@
// Copyright (c) 2019-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#define ANKERL_NANOBENCH_IMPLEMENT
#include <bench/nanobench.h>

3225
src/bench/nanobench.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -11,30 +11,31 @@ static constexpr uint64_t BUFFER_SIZE_TINY = 64;
static constexpr uint64_t BUFFER_SIZE_SMALL = 256;
static constexpr uint64_t BUFFER_SIZE_LARGE = 1024*1024;
static void POLY1305(benchmark::State& state, size_t buffersize)
static void POLY1305(benchmark::Bench& bench, size_t buffersize)
{
std::vector<unsigned char> tag(POLY1305_TAGLEN, 0);
std::vector<unsigned char> key(POLY1305_KEYLEN, 0);
std::vector<unsigned char> in(buffersize, 0);
while (state.KeepRunning())
bench.batch(in.size()).unit("byte").run([&] {
poly1305_auth(tag.data(), in.data(), in.size(), key.data());
});
}
static void POLY1305_64BYTES(benchmark::State& state)
static void POLY1305_64BYTES(benchmark::Bench& bench)
{
POLY1305(state, BUFFER_SIZE_TINY);
POLY1305(bench, BUFFER_SIZE_TINY);
}
static void POLY1305_256BYTES(benchmark::State& state)
static void POLY1305_256BYTES(benchmark::Bench& bench)
{
POLY1305(state, BUFFER_SIZE_SMALL);
POLY1305(bench, BUFFER_SIZE_SMALL);
}
static void POLY1305_1MB(benchmark::State& state)
static void POLY1305_1MB(benchmark::Bench& bench)
{
POLY1305(state, BUFFER_SIZE_LARGE);
POLY1305(bench, BUFFER_SIZE_LARGE);
}
BENCHMARK(POLY1305_64BYTES, 500000);
BENCHMARK(POLY1305_256BYTES, 250000);
BENCHMARK(POLY1305_1MB, 340);
BENCHMARK(POLY1305_64BYTES);
BENCHMARK(POLY1305_256BYTES);
BENCHMARK(POLY1305_1MB);

View file

@ -30,51 +30,44 @@ static_assert(IS_TRIVIALLY_CONSTRUCTIBLE<trivial_t>::value,
"expected trivial_t to be trivially constructible");
template <typename T>
static void PrevectorDestructor(benchmark::State& state)
static void PrevectorDestructor(benchmark::Bench& bench)
{
while (state.KeepRunning()) {
for (auto x = 0; x < 1000; ++x) {
prevector<28, T> t0;
prevector<28, T> t1;
t0.resize(28);
t1.resize(29);
}
}
}
template <typename T>
static void PrevectorClear(benchmark::State& state)
{
while (state.KeepRunning()) {
for (auto x = 0; x < 1000; ++x) {
prevector<28, T> t0;
prevector<28, T> t1;
t0.resize(28);
t0.clear();
t1.resize(29);
t1.clear();
}
}
}
template <typename T>
static void PrevectorResize(benchmark::State& state)
{
while (state.KeepRunning()) {
bench.batch(2).run([&] {
prevector<28, T> t0;
prevector<28, T> t1;
for (auto x = 0; x < 1000; ++x) {
t0.resize(28);
t0.resize(0);
t1.resize(29);
t1.resize(0);
}
}
t0.resize(28);
t1.resize(29);
});
}
template <typename T>
static void PrevectorDeserialize(benchmark::State& state)
static void PrevectorClear(benchmark::Bench& bench)
{
prevector<28, T> t0;
prevector<28, T> t1;
bench.batch(2).run([&] {
t0.resize(28);
t0.clear();
t1.resize(29);
t1.clear();
});
}
template <typename T>
static void PrevectorResize(benchmark::Bench& bench)
{
prevector<28, T> t0;
prevector<28, T> t1;
bench.batch(4).run([&] {
t0.resize(28);
t0.resize(0);
t1.resize(29);
t1.resize(0);
});
}
template <typename T>
static void PrevectorDeserialize(benchmark::Bench& bench)
{
CDataStream s0(SER_NETWORK, 0);
prevector<28, T> t0;
@ -86,26 +79,28 @@ static void PrevectorDeserialize(benchmark::State& state)
for (auto x = 0; x < 101; ++x) {
s0 << t0;
}
while (state.KeepRunning()) {
bench.batch(1000).run([&] {
prevector<28, T> t1;
for (auto x = 0; x < 1000; ++x) {
s0 >> t1;
}
s0.Init(SER_NETWORK, 0);
}
});
}
#define PREVECTOR_TEST(name, nontrivops, trivops) \
static void Prevector ## name ## Nontrivial(benchmark::State& state) { \
Prevector ## name<nontrivial_t>(state); \
} \
BENCHMARK(Prevector ## name ## Nontrivial, nontrivops); \
static void Prevector ## name ## Trivial(benchmark::State& state) { \
Prevector ## name<trivial_t>(state); \
} \
BENCHMARK(Prevector ## name ## Trivial, trivops);
#define PREVECTOR_TEST(name) \
static void Prevector##name##Nontrivial(benchmark::Bench& bench) \
{ \
Prevector##name<nontrivial_t>(bench); \
} \
BENCHMARK(Prevector##name##Nontrivial); \
static void Prevector##name##Trivial(benchmark::Bench& bench) \
{ \
Prevector##name<trivial_t>(bench); \
} \
BENCHMARK(Prevector##name##Trivial);
PREVECTOR_TEST(Clear, 28300, 88600)
PREVECTOR_TEST(Destructor, 28800, 88900)
PREVECTOR_TEST(Resize, 28900, 90300)
PREVECTOR_TEST(Deserialize, 6800, 52000)
PREVECTOR_TEST(Clear)
PREVECTOR_TEST(Destructor)
PREVECTOR_TEST(Resize)
PREVECTOR_TEST(Deserialize)

View file

@ -6,12 +6,12 @@
#include <bench/bench.h>
#include <bloom.h>
static void RollingBloom(benchmark::State& state)
static void RollingBloom(benchmark::Bench& bench)
{
CRollingBloomFilter filter(120000, 0.000001);
std::vector<unsigned char> data(32);
uint32_t count = 0;
while (state.KeepRunning()) {
bench.run([&] {
count++;
data[0] = count;
data[1] = count >> 8;
@ -24,16 +24,16 @@ static void RollingBloom(benchmark::State& state)
data[2] = count >> 8;
data[3] = count;
filter.contains(data);
}
});
}
static void RollingBloomReset(benchmark::State& state)
static void RollingBloomReset(benchmark::Bench& bench)
{
CRollingBloomFilter filter(120000, 0.000001);
while (state.KeepRunning()) {
bench.run([&] {
filter.reset();
}
});
}
BENCHMARK(RollingBloom, 1500 * 1000);
BENCHMARK(RollingBloomReset, 20000);
BENCHMARK(RollingBloom);
BENCHMARK(RollingBloomReset);

View file

@ -11,7 +11,8 @@
#include <univalue.h>
static void BlockToJsonVerbose(benchmark::State& state) {
static void BlockToJsonVerbose(benchmark::Bench& bench)
{
CDataStream stream(benchmark::data::block413567, SER_NETWORK, PROTOCOL_VERSION);
char a = '\0';
stream.write(&a, 1); // Prevent compaction
@ -24,9 +25,9 @@ static void BlockToJsonVerbose(benchmark::State& state) {
blockindex.phashBlock = &blockHash;
blockindex.nBits = 403014710;
while (state.KeepRunning()) {
bench.run([&] {
(void)blockToJSON(block, &blockindex, &blockindex, /*verbose*/ true);
}
});
}
BENCHMARK(BlockToJsonVerbose, 10);
BENCHMARK(BlockToJsonVerbose);

View file

@ -15,7 +15,7 @@ static void AddTx(const CTransactionRef& tx, const CAmount& fee, CTxMemPool& poo
pool.addUnchecked(CTxMemPoolEntry(tx, fee, /* time */ 0, /* height */ 1, /* spendsCoinbase */ false, /* sigOpCost */ 4, lp));
}
static void RpcMempool(benchmark::State& state)
static void RpcMempool(benchmark::Bench& bench)
{
CTxMemPool pool;
LOCK2(cs_main, pool.cs);
@ -32,9 +32,9 @@ static void RpcMempool(benchmark::State& state)
AddTx(tx_r, /* fee */ i, pool);
}
while (state.KeepRunning()) {
bench.run([&] {
(void)MempoolToJSON(pool, /*verbose*/ true);
}
});
}
BENCHMARK(RpcMempool, 40);
BENCHMARK(RpcMempool);

View file

@ -6,37 +6,37 @@
#include <util/time.h>
static void BenchTimeDeprecated(benchmark::State& state)
static void BenchTimeDeprecated(benchmark::Bench& bench)
{
while (state.KeepRunning()) {
bench.run([&] {
(void)GetTime();
}
});
}
static void BenchTimeMock(benchmark::State& state)
static void BenchTimeMock(benchmark::Bench& bench)
{
SetMockTime(111);
while (state.KeepRunning()) {
bench.run([&] {
(void)GetTime<std::chrono::seconds>();
}
});
SetMockTime(0);
}
static void BenchTimeMillis(benchmark::State& state)
static void BenchTimeMillis(benchmark::Bench& bench)
{
while (state.KeepRunning()) {
bench.run([&] {
(void)GetTime<std::chrono::milliseconds>();
}
});
}
static void BenchTimeMillisSys(benchmark::State& state)
static void BenchTimeMillisSys(benchmark::Bench& bench)
{
while (state.KeepRunning()) {
bench.run([&] {
(void)GetTimeMillis();
}
});
}
BENCHMARK(BenchTimeDeprecated, 100000000);
BENCHMARK(BenchTimeMillis, 6000000);
BENCHMARK(BenchTimeMillisSys, 6000000);
BENCHMARK(BenchTimeMock, 300000000);
BENCHMARK(BenchTimeDeprecated);
BENCHMARK(BenchTimeMillis);
BENCHMARK(BenchTimeMillisSys);
BENCHMARK(BenchTimeMock);

View file

@ -16,7 +16,7 @@
// Microbenchmark for verification of a basic P2WPKH script. Can be easily
// modified to measure performance of other types of scripts.
static void VerifyScriptBench(benchmark::State& state)
static void VerifyScriptBench(benchmark::Bench& bench)
{
const ECCVerifyHandle verify_handle;
ECC_Start();
@ -49,7 +49,7 @@ static void VerifyScriptBench(benchmark::State& state)
witness.stack.push_back(ToByteVector(pubkey));
// Benchmark.
while (state.KeepRunning()) {
bench.run([&] {
ScriptError err;
bool success = VerifyScript(
txSpend.vin[0].scriptSig,
@ -71,11 +71,12 @@ static void VerifyScriptBench(benchmark::State& state)
(const unsigned char*)stream.data(), stream.size(), 0, flags, nullptr);
assert(csuccess == 1);
#endif
}
});
ECC_Stop();
}
static void VerifyNestedIfScript(benchmark::State& state) {
static void VerifyNestedIfScript(benchmark::Bench& bench)
{
std::vector<std::vector<unsigned char>> stack;
CScript script;
for (int i = 0; i < 100; ++i) {
@ -87,15 +88,13 @@ static void VerifyNestedIfScript(benchmark::State& state) {
for (int i = 0; i < 100; ++i) {
script << OP_ENDIF;
}
while (state.KeepRunning()) {
bench.run([&] {
auto stack_copy = stack;
ScriptError error;
bool ret = EvalScript(stack_copy, script, 0, BaseSignatureChecker(), SigVersion::BASE, &error);
assert(ret);
}
});
}
BENCHMARK(VerifyScriptBench, 6300);
BENCHMARK(VerifyNestedIfScript, 100);
BENCHMARK(VerifyScriptBench);
BENCHMARK(VerifyNestedIfScript);

View file

@ -12,7 +12,7 @@
#include <validationinterface.h>
#include <wallet/wallet.h>
static void WalletBalance(benchmark::State& state, const bool set_dirty, const bool add_watchonly, const bool add_mine)
static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const bool add_watchonly, const bool add_mine)
{
TestingSetup test_setup{
CBaseChainParams::REGTEST,
@ -45,20 +45,20 @@ static void WalletBalance(benchmark::State& state, const bool set_dirty, const b
auto bal = wallet.GetBalance(); // Cache
while (state.KeepRunning()) {
bench.run([&] {
if (set_dirty) wallet.MarkDirty();
bal = wallet.GetBalance();
if (add_mine) assert(bal.m_mine_trusted > 0);
if (add_watchonly) assert(bal.m_watchonly_trusted > 0);
}
});
}
static void WalletBalanceDirty(benchmark::State& state) { WalletBalance(state, /* set_dirty */ true, /* add_watchonly */ true, /* add_mine */ true); }
static void WalletBalanceClean(benchmark::State& state) { WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ true); }
static void WalletBalanceMine(benchmark::State& state) { WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ false, /* add_mine */ true); }
static void WalletBalanceWatch(benchmark::State& state) { WalletBalance(state, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ false); }
static void WalletBalanceDirty(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ true, /* add_watchonly */ true, /* add_mine */ true); }
static void WalletBalanceClean(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ true); }
static void WalletBalanceMine(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ false, /* add_mine */ true); }
static void WalletBalanceWatch(benchmark::Bench& bench) { WalletBalance(bench, /* set_dirty */ false, /* add_watchonly */ true, /* add_mine */ false); }
BENCHMARK(WalletBalanceDirty, 2500);
BENCHMARK(WalletBalanceClean, 8000);
BENCHMARK(WalletBalanceMine, 16000);
BENCHMARK(WalletBalanceWatch, 8000);
BENCHMARK(WalletBalanceDirty);
BENCHMARK(WalletBalanceClean);
BENCHMARK(WalletBalanceMine);
BENCHMARK(WalletBalanceWatch);

View file

@ -10,7 +10,7 @@ export LC_ALL=C
HEADER_ID_PREFIX="BITCOIN_"
HEADER_ID_SUFFIX="_H"
REGEXP_EXCLUDE_FILES_WITH_PREFIX="src/(crypto/ctaes/|leveldb/|crc32c/|secp256k1/|test/fuzz/FuzzedDataProvider.h|tinyformat.h|univalue/)"
REGEXP_EXCLUDE_FILES_WITH_PREFIX="src/(crypto/ctaes/|leveldb/|crc32c/|secp256k1/|test/fuzz/FuzzedDataProvider.h|tinyformat.h|bench/nanobench.h|univalue/)"
EXIT_CODE=0
for HEADER_FILE in $(git ls-files -- "*.h" | grep -vE "^${REGEXP_EXCLUDE_FILES_WITH_PREFIX}")