Changeset View
Changeset View
Standalone View
Standalone View
src/bench/bench.cpp
// Copyright (c) 2015-2016 The Bitcoin Core developers | // Copyright (c) 2015-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include "bench.h" | #include "bench.h" | ||||
#include "perf.h" | #include "perf.h" | ||||
#include <iomanip> | #include <iomanip> | ||||
#include <iostream> | #include <iostream> | ||||
#include <sys/time.h> | |||||
benchmark::BenchRunner::BenchmarkMap &benchmark::BenchRunner::benchmarks() { | benchmark::BenchRunner::BenchmarkMap &benchmark::BenchRunner::benchmarks() { | ||||
static std::map<std::string, benchmark::BenchFunction> benchmarks_map; | static std::map<std::string, benchmark::BenchFunction> benchmarks_map; | ||||
return benchmarks_map; | return benchmarks_map; | ||||
} | } | ||||
static double gettimedouble(void) { | |||||
struct timeval tv; | |||||
gettimeofday(&tv, nullptr); | |||||
return tv.tv_usec * 0.000001 + tv.tv_sec; | |||||
} | |||||
benchmark::BenchRunner::BenchRunner(std::string name, | benchmark::BenchRunner::BenchRunner(std::string name, | ||||
benchmark::BenchFunction func) { | benchmark::BenchFunction func) { | ||||
benchmarks().insert(std::make_pair(name, func)); | benchmarks().insert(std::make_pair(name, func)); | ||||
} | } | ||||
void benchmark::BenchRunner::RunAll(double elapsedTimeForOne) { | void benchmark::BenchRunner::RunAll(benchmark::duration elapsedTimeForOne) { | ||||
perf_init(); | perf_init(); | ||||
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"; | |||||
} | |||||
std::cout << "#Benchmark" | std::cout << "#Benchmark" | ||||
<< "," | << "," | ||||
<< "count" | << "count" | ||||
<< "," | << "," | ||||
<< "min" | << "min(ns)" | ||||
<< "," | << "," | ||||
<< "max" | << "max(ns)" | ||||
<< "," | << "," | ||||
<< "average" | << "average(ns)" | ||||
<< "," | << "," | ||||
<< "min_cycles" | << "min_cycles" | ||||
<< "," | << "," | ||||
<< "max_cycles" | << "max_cycles" | ||||
<< "," | << "," | ||||
<< "average_cycles" | << "average_cycles" | ||||
<< "\n"; | << "\n"; | ||||
for (const auto &p : benchmarks()) { | for (const auto &p : benchmarks()) { | ||||
State state(p.first, elapsedTimeForOne); | State state(p.first, elapsedTimeForOne); | ||||
p.second(state); | p.second(state); | ||||
} | } | ||||
perf_fini(); | perf_fini(); | ||||
} | } | ||||
bool benchmark::State::KeepRunning() { | bool benchmark::State::KeepRunning() { | ||||
if (count & countMask) { | if (count & countMask) { | ||||
++count; | ++count; | ||||
return true; | return true; | ||||
} | } | ||||
double now; | time_point now; | ||||
uint64_t nowCycles; | uint64_t nowCycles; | ||||
if (count == 0) { | if (count == 0) { | ||||
lastTime = beginTime = now = gettimedouble(); | lastTime = beginTime = now = clock::now(); | ||||
lastCycles = beginCycles = nowCycles = perf_cpucycles(); | lastCycles = beginCycles = nowCycles = perf_cpucycles(); | ||||
} else { | } else { | ||||
now = gettimedouble(); | now = clock::now(); | ||||
double elapsed = now - lastTime; | auto elapsed = now - lastTime; | ||||
double elapsedOne = elapsed * countMaskInv; | auto elapsedOne = elapsed / (countMask + 1); | ||||
if (elapsedOne < minTime) minTime = elapsedOne; | if (elapsedOne < minTime) minTime = elapsedOne; | ||||
if (elapsedOne > maxTime) maxTime = elapsedOne; | if (elapsedOne > maxTime) maxTime = elapsedOne; | ||||
// We only use relative values, so don't have to handle 64-bit | // We only use relative values, so don't have to handle 64-bit | ||||
// wrap-around specially | // wrap-around specially | ||||
nowCycles = perf_cpucycles(); | nowCycles = perf_cpucycles(); | ||||
uint64_t elapsedOneCycles = (nowCycles - lastCycles) * countMaskInv; | uint64_t elapsedOneCycles = (nowCycles - lastCycles) / (countMask + 1); | ||||
if (elapsedOneCycles < minCycles) minCycles = elapsedOneCycles; | if (elapsedOneCycles < minCycles) minCycles = elapsedOneCycles; | ||||
if (elapsedOneCycles > maxCycles) maxCycles = elapsedOneCycles; | if (elapsedOneCycles > maxCycles) maxCycles = elapsedOneCycles; | ||||
if (elapsed * 128 < maxElapsed) { | if (elapsed * 128 < maxElapsed) { | ||||
// If the execution was much too fast (1/128th of maxElapsed), | // If the execution was much too fast (1/128th of maxElapsed), | ||||
// increase the count mask by 8x and restart timing. | // increase the count mask by 8x and restart timing. | ||||
// The restart avoids including the overhead of this code in the | // The restart avoids including the overhead of this code in the | ||||
// measurement. | // measurement. | ||||
countMask = ((countMask << 3) | 7) & ((1LL << 60) - 1); | countMask = ((countMask << 3) | 7) & ((1LL << 60) - 1); | ||||
countMaskInv = 1. / (countMask + 1); | |||||
count = 0; | count = 0; | ||||
minTime = std::numeric_limits<double>::max(); | minTime = duration::max(); | ||||
maxTime = std::numeric_limits<double>::min(); | maxTime = duration::zero(); | ||||
minCycles = std::numeric_limits<uint64_t>::max(); | minCycles = std::numeric_limits<uint64_t>::max(); | ||||
maxCycles = std::numeric_limits<uint64_t>::min(); | maxCycles = std::numeric_limits<uint64_t>::min(); | ||||
return true; | return true; | ||||
} | } | ||||
if (elapsed * 16 < maxElapsed) { | if (elapsed * 16 < maxElapsed) { | ||||
uint64_t newCountMask = ((countMask << 1) | 1) & ((1LL << 60) - 1); | uint64_t newCountMask = ((countMask << 1) | 1) & ((1LL << 60) - 1); | ||||
if ((count & newCountMask) == 0) { | if ((count & newCountMask) == 0) { | ||||
countMask = newCountMask; | countMask = newCountMask; | ||||
countMaskInv = 1. / (countMask + 1); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
lastTime = now; | lastTime = now; | ||||
lastCycles = nowCycles; | lastCycles = nowCycles; | ||||
++count; | ++count; | ||||
if (now - beginTime < maxElapsed) return true; // Keep going | if (now - beginTime < maxElapsed) return true; // Keep going | ||||
--count; | --count; | ||||
// Output results | // Output results | ||||
double average = (now - beginTime) / count; | // Duration casts are only necessary here because hardware with | ||||
// sub-nanosecond clocks | |||||
// will lose precision. | |||||
int64_t min_elapsed = | |||||
std::chrono::duration_cast<std::chrono::nanoseconds>(minTime).count(); | |||||
int64_t max_elapsed = | |||||
std::chrono::duration_cast<std::chrono::nanoseconds>(maxTime).count(); | |||||
int64_t avg_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>( | |||||
(now - beginTime) / count) | |||||
.count(); | |||||
int64_t averageCycles = (nowCycles - beginCycles) / count; | int64_t averageCycles = (nowCycles - beginCycles) / count; | ||||
std::cout << std::fixed << std::setprecision(15) << name << "," << count | std::cout << std::fixed << std::setprecision(15) << name << "," << count | ||||
<< "," << minTime << "," << maxTime << "," << average << "," | << "," << min_elapsed << "," << max_elapsed << "," << avg_elapsed | ||||
<< minCycles << "," << maxCycles << "," << averageCycles << "\n"; | << "," << minCycles << "," << maxCycles << "," << averageCycles | ||||
<< "\n"; | |||||
return false; | return false; | ||||
} | } |