mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-20 14:05:23 +01:00
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 * Adds support for asymptotes This adds support to calculate asymptotic complexity of a benchmark. This is similar to #17375, but currently only one asymptote is supported, and I have added support in the benchmark `ComplexMemPool` as an example. Usage is e.g. like this: ``` ./bench_bitcoin -filter=ComplexMemPool -asymptote=25,50,100,200,400,600,800 ``` This runs the benchmark `ComplexMemPool` several times but with different complexityN settings. The benchmark can extract that number and use it accordingly. Here, it's used for `childTxs`. The output is this: | complexityN | ns/op | op/s | err% | ins/op | cyc/op | IPC | total | benchmark |------------:|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|----------:|:---------- | 25 | 1,064,241.00 | 939.64 | 1.4% | 3,960,279.00 | 2,829,708.00 | 1.400 | 0.01 | `ComplexMemPool` | 50 | 1,579,530.00 | 633.10 | 1.0% | 6,231,810.00 | 4,412,674.00 | 1.412 | 0.02 | `ComplexMemPool` | 100 | 4,022,774.00 | 248.58 | 0.6% | 16,544,406.00 | 11,889,535.00 | 1.392 | 0.04 | `ComplexMemPool` | 200 | 15,390,986.00 | 64.97 | 0.2% | 63,904,254.00 | 47,731,705.00 | 1.339 | 0.17 | `ComplexMemPool` | 400 | 69,394,711.00 | 14.41 | 0.1% | 272,602,461.00 | 219,014,691.00 | 1.245 | 0.76 | `ComplexMemPool` | 600 | 168,977,165.00 | 5.92 | 0.1% | 639,108,082.00 | 535,316,887.00 | 1.194 | 1.86 | `ComplexMemPool` | 800 | 310,109,077.00 | 3.22 | 0.1% |1,149,134,246.00 | 984,620,812.00 | 1.167 | 3.41 | `ComplexMemPool` | coefficient | err% | complexity |--------------:|-------:|------------ | 4.78486e-07 | 4.5% | O(n^2) | 6.38557e-10 | 21.7% | O(n^3) | 3.42338e-05 | 38.0% | O(n log n) | 0.000313914 | 46.9% | O(n) | 0.0129823 | 114.4% | O(log n) | 0.0815055 | 133.8% | O(1) The best fitting curve is O(n^2), so the algorithm seems to scale quadratic with `childTxs` in the range 25 to 800.
This commit is contained in:
parent
19e919217e
commit
78c312c983
38 changed files with 3656 additions and 585 deletions
|
@ -75,7 +75,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.
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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`
|
||||
...
|
||||
```
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -1151,8 +1151,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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -9,26 +9,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)
|
||||
|
@ -47,34 +51,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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
6
src/bench/nanobench.cpp
Normal 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
3225
src/bench/nanobench.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}")
|
||||
|
|
Loading…
Add table
Reference in a new issue