Miscellany / Timing and Benchmarking
8.4 Timing and Benchmarking
8-Miscellany/8.4_Timing_and_Benchmarking.cpp
Small timing helpers for local profiling and codebook examples. Use std::chrono::steady_clock instead of wall-clock time so elapsed durations are monotonic.
Timer()starts a timer immediately.timer.reset()restarts the timer.timer.elapsed()returns the elapsed time in milliseconds as a double.benchmark(name, iterations, f)runsf()iterationstimes and returns the average elapsed milliseconds per call with the given name.
Benchmarks should be treated as local measurements, not proofs of asymptotic performance. Warm-up, CPU frequency scaling, compiler optimization, and input choice can dominate tiny timings.
Implementation
#include <cassert>
#include <chrono>
#include <numeric>
#include <string>
#include <utility>
#include <vector>
class Timer {
using Clock = std::chrono::steady_clock;
Clock::time_point start_time;
public:
Timer() { reset(); }
void reset() { start_time = Clock::now(); }
double elapsed() const {
std::chrono::duration<double, std::milli> diff = Clock::now() - start_time;
return diff.count();
}
};
template<class Fun>
std::pair<std::string, double> benchmark(const std::string &name, int iterations, Fun f) {
Timer timer;
for (int i = 0; i < iterations; ++i) {
f();
}
return {name, timer.elapsed() / iterations};
}
Example Usage
int main() {
Timer timer;
std::vector<int> v(1000);
std::iota(v.begin(), v.end(), 0);
assert(timer.elapsed() >= 0.0);
volatile long long sink = 0;
auto res = benchmark("sum", 5, [&] {
long long cur = 0;
for (int x : v) {
cur += x;
}
sink += cur;
});
assert(res.first == "sum");
assert(res.second >= 0.0);
assert(sink > 0);
return 0;
}
/*
Small timing helpers for local profiling and codebook examples. Use `std::chrono::steady_clock`
instead of wall-clock time so elapsed durations are monotonic.
- `Timer()` starts a timer immediately.
- `timer.reset()` restarts the timer.
- `timer.elapsed()` returns the elapsed time in milliseconds as a double.
- `benchmark(name, iterations, f)` runs `f()` `iterations` times and returns the average elapsed
milliseconds per call with the given name.
Benchmarks should be treated as local measurements, not proofs of asymptotic performance. Warm-up,
CPU frequency scaling, compiler optimization, and input choice can dominate tiny timings.
*/
#include <cassert>
#include <chrono>
#include <numeric>
#include <string>
#include <utility>
#include <vector>
class Timer {
using Clock = std::chrono::steady_clock;
Clock::time_point start_time;
public:
Timer() { reset(); }
void reset() { start_time = Clock::now(); }
double elapsed() const {
std::chrono::duration<double, std::milli> diff = Clock::now() - start_time;
return diff.count();
}
};
template<class Fun>
std::pair<std::string, double> benchmark(const std::string &name, int iterations, Fun f) {
Timer timer;
for (int i = 0; i < iterations; ++i) {
f();
}
return {name, timer.elapsed() / iterations};
}
/*** Example Usage ***/
int main() {
Timer timer;
std::vector<int> v(1000);
std::iota(v.begin(), v.end(), 0);
assert(timer.elapsed() >= 0.0);
volatile long long sink = 0;
auto res = benchmark("sum", 5, [&] {
long long cur = 0;
for (int x : v) {
cur += x;
}
sink += cur;
});
assert(res.first == "sum");
assert(res.second >= 0.0);
assert(sink > 0);
return 0;
}