forked from johnsalmon/cpp-counter-based-engine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtimeit.hpp
75 lines (69 loc) · 2.59 KB
/
timeit.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#pragma once
// inspired by Python's timeit utility...
//
// timeit(dur, func) - Call func repeatedly for specified duration.
// Report how many times the function was called, and how much time
// was spent (which is close, but not exactly equal to the specified
// duration).
//
// Usage:
//
// #include <core123/timeit.hpp>
//
// auto result = timeit(std::chrono::seconds(1),
// [](){
// ... do something ...
// });
// std::cout << "lambda was called " << result.count << " times in about 1s\n";
// std::cout << "Actually, the elapsed time was exactly: " << dur2string(result.dur) << " seconds\n";
//
// You can pass any no-argument functor at all to timeit: a plain-old
// function, a class with an operator()(), or a lambda (as above).
//
// Note that if f() has no side-effects, it's *possible* for an
// aggressive optimizer to completely elide calls to it, resulting in
// timings that are not representative.
//
#include <atomic>
#include <chrono>
#include <thread>
// N.b. use the high_resolution_clock, even though it might not be
// 'steady'. Timeit is typically used for short timing runs
// O(seconds), and we're more interested in high accuracy than we are
// worried about the clock's steadiness.
using clk_t = std::chrono::high_resolution_clock;
struct timeit_result{
long long count;
clk_t::duration dur;
timeit_result(long long count_, const clk_t::duration& dur_):
count(count_), dur(dur_)
{}
timeit_result() : timeit_result(0, {}){}
float iter_per_sec() const { return count/std::chrono::duration<float>(dur).count(); }
float sec_per_iter() const { return std::chrono::duration<float>(dur).count()/count; }
};
template <class Rep, class Period, class Functor>
timeit_result
timeit(const std::chrono::duration<Rep, Period>& dur, Functor f){
std::atomic<bool> done(false);
std::thread t( [&](){
std::this_thread::sleep_for(dur);
done.store(1);
});
long long n = 0;
auto start = clk_t::now();
do{
// Unrolling this produced very confusing results. Any f()
// that's fast enough that the overhead of n++ and
// !done.load() is significant seems to also be small enough
// that wrapping it in a loop (even with constant bounds)) is
// noticeably different from manual unrolling, Duff's device,
// etc. Let's just "keep it simple". The caller can unroll
// inside f() if it matters...
f();
n++;
}while(!done.load());
auto elapsed = clk_t::now() - start;
t.join();
return {n, elapsed};
}