Browse Source

Merge #11562: bench: use std::chrono rather than gettimeofday

24a0bdd bench: prefer a steady clock if the resolution is no worse (Cory Fields)
c515d26 bench: switch to std::chrono for time measurements (Cory Fields)

Pull request description:

  gettimeofday has portability issues, see for example #11558.

  Regardless of large-scale clock refactors in the future, I think it's fine for bench to just use std::chrono itself.

  Note that this may slightly improve bench accuracy and changes the display from tiny floats to nanosecond counts instead.

Tree-SHA512: 122355456d01ec6cfcf6867991715cf3a95eabbf5a4f2adc26a059b50382ffb318b7639cdd575197fc4ee5be8b967c0404f1f920d6f5bd4ddd0bd63b5e5c5632
0.16
Wladimir J. van der Laan 7 years ago
parent
commit
5776582b7f
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D
  1. 34
      src/bench/bench.cpp
  2. 26
      src/bench/bench.h
  3. 7
      src/bench/rollingbloom.cpp

34
src/bench/bench.cpp

@ -8,29 +8,22 @@
#include <assert.h> #include <assert.h>
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#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::BenchFunction func) benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction func)
{ {
benchmarks().insert(std::make_pair(name, func)); benchmarks().insert(std::make_pair(name, func));
} }
void void
benchmark::BenchRunner::RunAll(double elapsedTimeForOne) benchmark::BenchRunner::RunAll(benchmark::duration elapsedTimeForOne)
{ {
perf_init(); perf_init();
std::cout << "#Benchmark" << "," << "count" << "," << "min" << "," << "max" << "," << "average" << "," std::cout << "#Benchmark" << "," << "count" << "," << "min(ns)" << "," << "max(ns)" << "," << "average(ns)" << ","
<< "min_cycles" << "," << "max_cycles" << "," << "average_cycles" << "\n"; << "min_cycles" << "," << "max_cycles" << "," << "average_cycles" << "\n";
for (const auto &p: benchmarks()) { for (const auto &p: benchmarks()) {
@ -46,16 +39,17 @@ bool benchmark::State::KeepRunning()
++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 / (countMask + 1); 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;
@ -70,8 +64,8 @@ bool benchmark::State::KeepRunning()
// The restart avoids including the overhead of this code in the measurement. // The restart avoids including the overhead of this code in the measurement.
countMask = ((countMask<<3)|7) & ((1LL<<60)-1); countMask = ((countMask<<3)|7) & ((1LL<<60)-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;
@ -94,9 +88,13 @@ bool benchmark::State::KeepRunning()
assert(count != 0 && "count == 0 => (now == 0 && beginTime == 0) => return above"); assert(count != 0 && "count == 0 => (now == 0 && beginTime == 0) => return above");
// 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 << "," << minTime << "," << maxTime << "," << average << "," std::cout << std::fixed << std::setprecision(15) << name << "," << count << "," << min_elapsed << "," << max_elapsed << "," << avg_elapsed << ","
<< minCycles << "," << maxCycles << "," << averageCycles << "\n"; << minCycles << "," << maxCycles << "," << averageCycles << "\n";
std::cout.copyfmt(std::ios(nullptr)); std::cout.copyfmt(std::ios(nullptr));

26
src/bench/bench.h

@ -9,6 +9,7 @@
#include <limits> #include <limits>
#include <map> #include <map>
#include <string> #include <string>
#include <chrono>
#include <boost/preprocessor/cat.hpp> #include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp> #include <boost/preprocessor/stringize.hpp>
@ -36,12 +37,23 @@ BENCHMARK(CODE_TO_TIME);
*/ */
namespace benchmark { namespace benchmark {
// On many systems, the high_resolution_clock offers no better resolution than the steady_clock.
// If that's the case, prefer the steady_clock.
struct best_clock {
using hi_res_clock = std::chrono::high_resolution_clock;
using steady_clock = std::chrono::steady_clock;
static constexpr bool steady_is_high_res = std::ratio_less_equal<steady_clock::period, hi_res_clock::period>::value;
using type = std::conditional<steady_is_high_res, steady_clock, hi_res_clock>::type;
};
using clock = best_clock::type;
using time_point = clock::time_point;
using duration = clock::duration;
class State { class State {
std::string name; std::string name;
double maxElapsed; duration maxElapsed;
double beginTime; time_point beginTime, lastTime;
double lastTime, minTime, maxTime; duration minTime, maxTime;
uint64_t count; uint64_t count;
uint64_t countMask; uint64_t countMask;
uint64_t beginCycles; uint64_t beginCycles;
@ -49,9 +61,9 @@ namespace benchmark {
uint64_t minCycles; uint64_t minCycles;
uint64_t maxCycles; uint64_t maxCycles;
public: public:
State(std::string _name, double _maxElapsed) : name(_name), maxElapsed(_maxElapsed), count(0) { State(std::string _name, duration _maxElapsed) : name(_name), maxElapsed(_maxElapsed), 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();
countMask = 1; countMask = 1;
@ -69,7 +81,7 @@ namespace benchmark {
public: public:
BenchRunner(std::string name, BenchFunction func); BenchRunner(std::string name, BenchFunction func);
static void RunAll(double elapsedTimeForOne=1.0); static void RunAll(duration elapsedTimeForOne = std::chrono::seconds(1));
}; };
} }

7
src/bench/rollingbloom.cpp

@ -6,7 +6,6 @@
#include "bench.h" #include "bench.h"
#include "bloom.h" #include "bloom.h"
#include "utiltime.h"
static void RollingBloom(benchmark::State& state) static void RollingBloom(benchmark::State& state)
{ {
@ -23,10 +22,10 @@ static void RollingBloom(benchmark::State& state)
data[2] = count >> 16; data[2] = count >> 16;
data[3] = count >> 24; data[3] = count >> 24;
if (countnow == nEntriesPerGeneration) { if (countnow == nEntriesPerGeneration) {
int64_t b = GetTimeMicros(); auto b = benchmark::clock::now();
filter.insert(data); filter.insert(data);
int64_t e = GetTimeMicros(); auto total = std::chrono::duration_cast<std::chrono::nanoseconds>(benchmark::clock::now() - b).count();
std::cout << "RollingBloom-refresh,1," << (e-b)*0.000001 << "," << (e-b)*0.000001 << "," << (e-b)*0.000001 << "\n"; std::cout << "RollingBloom-refresh,1," << total << "," << total << "," << total << "\n";
countnow = 0; countnow = 0;
} else { } else {
filter.insert(data); filter.insert(data);

Loading…
Cancel
Save