Skip to content

Commit 38367b1

Browse files
committed
bench: switch to std::chrono for time measurements
- backports bitcoin/bitcoin@c515d26 std::chrono removes portability issues. Rather than storing doubles, store the untouched time_points. Then convert to nanoseconds for display. This allows for maximum precision, while keeping results comparable between differing hardware/operating systems. Also, display full nanosecond counts rather than sub-second floats.
1 parent a24633a commit 38367b1

File tree

2 files changed

+27
-25
lines changed

2 files changed

+27
-25
lines changed

src/bench/bench.cpp

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,22 @@
99
#include <assert.h>
1010
#include <iomanip>
1111
#include <iostream>
12-
#include <sys/time.h>
1312

1413
benchmark::BenchRunner::BenchmarkMap &benchmark::BenchRunner::benchmarks() {
1514
static std::map<std::string, benchmark::BenchFunction> benchmarks_map;
1615
return benchmarks_map;
1716
}
1817

19-
static double gettimedouble(void) {
20-
struct timeval tv;
21-
gettimeofday(&tv, NULL);
22-
return tv.tv_usec * 0.000001 + tv.tv_sec;
23-
}
24-
2518
benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction func)
2619
{
2720
benchmarks().insert(std::make_pair(name, func));
2821
}
2922

3023
void
31-
benchmark::BenchRunner::RunAll(double elapsedTimeForOne)
24+
benchmark::BenchRunner::RunAll(benchmark::duration elapsedTimeForOne)
3225
{
3326
perf_init();
34-
std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << ","
27+
std::cout << "#Benchmark" << "," << "count" << "," << "min(ns)" << "," << "max(ns)" << "," << "average(ns)" << ","
3528
<< "min_cycles" << "," << "max_cycles" << "," << "average_cycles" << "\n";
3629

3730
for (const auto &p: benchmarks()) {
@@ -47,16 +40,16 @@ bool benchmark::State::KeepRunning()
4740
++count;
4841
return true;
4942
}
50-
double now;
43+
time_point now;
5144
uint64_t nowCycles;
5245
if (count == 0) {
53-
beginTime = now = gettimedouble();
46+
beginTime = now = clock::now();
5447
lastCycles = beginCycles = nowCycles = perf_cpucycles();
5548
}
5649
else {
57-
now = gettimedouble();
58-
double elapsed = now - lastTime;
59-
double elapsedOne = elapsed / (countMask + 1);
50+
now = clock::now();
51+
auto elapsed = now - lastTime;
52+
auto elapsedOne = elapsed / (countMask + 1);
6053
if (elapsedOne < minTime) minTime = elapsedOne;
6154
if (elapsedOne > maxTime) maxTime = elapsedOne;
6255

@@ -71,8 +64,8 @@ bool benchmark::State::KeepRunning()
7164
// The restart avoids including the overhead of this code in the measurement.
7265
countMask = ((countMask<<3)|7) & ((1LL<<60)-1);
7366
count = 0;
74-
minTime = std::numeric_limits<double>::max();
75-
maxTime = std::numeric_limits<double>::min();
67+
minTime = duration::max();
68+
maxTime = duration::zero();
7669
minCycles = std::numeric_limits<uint64_t>::max();
7770
maxCycles = std::numeric_limits<uint64_t>::min();
7871
return true;
@@ -95,9 +88,13 @@ bool benchmark::State::KeepRunning()
9588
assert(count != 0 && "count == 0 => (now == 0 && beginTime == 0) => return above");
9689

9790
// Output results
98-
double average = (now-beginTime)/count;
91+
// Duration casts are only necessary here because hardware with sub-nanosecond clocks
92+
// will lose precision.
93+
int64_t min_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(minTime).count();
94+
int64_t max_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(maxTime).count();
95+
int64_t avg_elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>((now-beginTime)/count).count();
9996
int64_t averageCycles = (nowCycles-beginCycles)/count;
100-
std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << minTime << "," << maxTime << "," << average << ","
97+
std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << min_elapsed << "," << max_elapsed << "," << avg_elapsed << ","
10198
<< minCycles << "," << maxCycles << "," << averageCycles << "\n";
10299
std::cout.copyfmt(std::ios(nullptr));
103100

src/bench/bench.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef BITCOIN_BENCH_BENCH_H
66
#define BITCOIN_BENCH_BENCH_H
77

8+
#include <chrono>
89
#include <functional>
910
#include <limits>
1011
#include <map>
@@ -35,21 +36,25 @@ BENCHMARK(CODE_TO_TIME);
3536

3637
namespace benchmark {
3738

39+
using clock = std::chrono::high_resolution_clock;
40+
using time_point = clock::time_point;
41+
using duration = clock::duration;
42+
3843
class State {
3944
std::string name;
40-
double maxElapsed;
41-
double beginTime;
42-
double lastTime, minTime, maxTime;
45+
duration maxElapsed;
46+
time_point beginTime, lastTime;
47+
duration minTime, maxTime;
4348
uint64_t count;
4449
uint64_t countMask;
4550
uint64_t beginCycles;
4651
uint64_t lastCycles;
4752
uint64_t minCycles;
4853
uint64_t maxCycles;
4954
public:
50-
State(std::string _name, double _maxElapsed) : name(_name), maxElapsed(_maxElapsed), count(0) {
51-
minTime = std::numeric_limits<double>::max();
52-
maxTime = std::numeric_limits<double>::min();
55+
State(std::string _name, duration _maxElapsed) : name(_name), maxElapsed(_maxElapsed), count(0) {
56+
minTime = duration::max();
57+
maxTime = duration::zero();
5358
minCycles = std::numeric_limits<uint64_t>::max();
5459
maxCycles = std::numeric_limits<uint64_t>::min();
5560
countMask = 1;
@@ -67,7 +72,7 @@ namespace benchmark {
6772
public:
6873
BenchRunner(std::string name, BenchFunction func);
6974

70-
static void RunAll(double elapsedTimeForOne=1.0);
75+
static void RunAll(duration elapsedTimeForOne = std::chrono::seconds(1));
7176
};
7277
}
7378

0 commit comments

Comments
 (0)