1 // Written in the D programming language
4 Module containing some basic benchmarking and timing functionality.
6 For convenience, this module publicly imports $(MREF core,time).
8 $(RED Unlike the other modules in std.datetime, this module is not currently
9 publicly imported in std.datetime.package, because the old
10 versions of this functionality which use
11 $(REF TickDuration,core,time) are in std.datetime.package and would
12 conflict with the symbols in this module. After the old symbols have
13 gone through the deprecation cycle and have been removed, then this
14 module will be publicly imported in std.datetime.package.)
16 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
17 Authors: Jonathan M Davis and Kato Shoichi
18 Source: $(PHOBOSSRC std/datetime/_stopwatch.d)
20 module std
.datetime
.stopwatch
;
22 public import core
.time
;
23 import std
.typecons
: Flag
;
26 Used by StopWatch to indicate whether it should start immediately upon
29 If set to $(D AutoStart.no), then the StopWatch is not started when it is
32 Otherwise, if set to $(D AutoStart.yes), then the StopWatch is started when
35 alias AutoStart
= Flag
!"autoStart";
39 StopWatch is used to measure time just like one would do with a physical
40 stopwatch, including stopping, restarting, and/or resetting it.
42 $(REF MonoTime,core,time) is used to hold the time, and it uses the system's
43 monotonic clock, which is high precision and never counts backwards (unlike
44 the wall clock time, which $(I can) count backwards, which is why
45 $(REF SysTime,std,datetime,systime) should not be used for timing).
47 Note that the precision of StopWatch differs from system to system. It is
48 impossible for it to be the same for all systems, since the precision of the
49 system clock and other system-dependent and situation-dependent factors
50 (such as the overhead of a context switch between threads) varies from system
51 to system and can affect StopWatch's accuracy.
58 @system nothrow @nogc unittest
60 import core
.thread
: Thread
;
62 auto sw
= StopWatch(AutoStart
.yes
);
64 Duration t1
= sw
.peek();
65 Thread
.sleep(usecs(1));
66 Duration t2
= sw
.peek();
69 Thread
.sleep(usecs(1));
72 Duration t3
= sw
.peek();
74 Duration t4
= sw
.peek();
78 Thread
.sleep(usecs(1));
80 Duration t5
= sw
.peek();
83 // If stopping or resetting the StopWatch is not required, then
84 // MonoTime can easily be used by itself without StopWatch.
85 auto before
= MonoTime
.currTime
;
87 auto timeElapsed
= MonoTime
.currTime
- before
;
91 Constructs a StopWatch. Whether it starts immediately depends on the
92 $(LREF AutoStart) argument.
94 If $(D StopWatch.init) is used, then the constructed StopWatch isn't
95 running (and can't be, since no constructor ran).
97 this(AutoStart autostart
) @safe nothrow @nogc
104 @system nothrow @nogc unittest
106 import core
.thread
: Thread
;
109 auto sw
= StopWatch(AutoStart
.yes
);
111 Thread
.sleep(usecs(1));
112 assert(sw
.peek() > Duration
.zero
);
115 auto sw
= StopWatch(AutoStart
.no
);
117 Thread
.sleep(usecs(1));
118 assert(sw
.peek() == Duration
.zero
);
123 Thread
.sleep(usecs(1));
124 assert(sw
.peek() == Duration
.zero
);
127 assert(StopWatch
.init
== StopWatch(AutoStart
.no
));
128 assert(StopWatch
.init
!= StopWatch(AutoStart
.yes
));
133 Resets the StopWatch.
135 The StopWatch can be reset while it's running, and resetting it while
136 it's running will not cause it to stop.
138 void reset() @safe nothrow @nogc
141 _timeStarted
= MonoTime
.currTime
;
146 @system nothrow @nogc unittest
148 import core
.thread
: Thread
;
150 auto sw
= StopWatch(AutoStart
.yes
);
151 Thread
.sleep(usecs(1));
153 assert(sw
.peek() > Duration
.zero
);
155 assert(sw
.peek() == Duration
.zero
);
158 @system nothrow @nogc unittest
160 import core
.thread
: Thread
;
162 auto sw
= StopWatch(AutoStart
.yes
);
163 Thread
.sleep(msecs(1));
164 assert(sw
.peek() > msecs(1));
165 immutable before
= MonoTime
.currTime
;
167 // Just in case the system clock is slow enough or the system is fast
168 // enough for the call to MonoTime.currTime inside of reset to get
169 // the same that we just got by calling MonoTime.currTime.
170 Thread
.sleep(usecs(1));
173 assert(sw
.peek() < msecs(1));
174 assert(sw
._timeStarted
> before
);
175 assert(sw
._timeStarted
<= MonoTime
.currTime
);
180 Starts the StopWatch.
182 start should not be called if the StopWatch is already running.
184 void start() @safe nothrow @nogc
185 in { assert(!_running
, "start was called when the StopWatch was already running."); }
189 _timeStarted
= MonoTime
.currTime
;
193 @system nothrow @nogc unittest
195 import core
.thread
: Thread
;
199 assert(sw
.peek() == Duration
.zero
);
202 Thread
.sleep(usecs(1));
203 assert(sw
.peek() > Duration
.zero
);
210 stop should not be called if the StopWatch is not running.
212 void stop() @safe nothrow @nogc
213 in { assert(_running
, "stop was called when the StopWatch was not running."); }
217 _ticksElapsed
+= MonoTime
.currTime
.ticks
- _timeStarted
.ticks
;
221 @system nothrow @nogc unittest
223 import core
.thread
: Thread
;
225 auto sw
= StopWatch(AutoStart
.yes
);
227 Thread
.sleep(usecs(1));
228 immutable t1
= sw
.peek();
229 assert(t1
> Duration
.zero
);
233 immutable t2
= sw
.peek();
235 immutable t3
= sw
.peek();
241 Peek at the amount of time that the the StopWatch has been running.
243 This does not include any time during which the StopWatch was stopped but
244 does include $(I all) of the time that it was running and not just the
245 time since it was started last.
247 Calling $(LREF reset) will reset this to $(D Duration.zero).
249 Duration
peek() @safe const nothrow @nogc
251 enum hnsecsPerSecond
= convert
!("seconds", "hnsecs")(1);
252 immutable hnsecsMeasured
= convClockFreq(_ticksElapsed
, MonoTime
.ticksPerSecond
, hnsecsPerSecond
);
253 return _running ? MonoTime
.currTime
- _timeStarted
+ hnsecs(hnsecsMeasured
)
254 : hnsecs(hnsecsMeasured
);
258 @system nothrow @nogc unittest
260 import core
.thread
: Thread
;
262 auto sw
= StopWatch(AutoStart
.no
);
263 assert(sw
.peek() == Duration
.zero
);
266 Thread
.sleep(usecs(1));
267 assert(sw
.peek() >= usecs(1));
269 Thread
.sleep(usecs(1));
270 assert(sw
.peek() >= usecs(2));
273 immutable stopped
= sw
.peek();
274 Thread
.sleep(usecs(1));
275 assert(sw
.peek() == stopped
);
278 Thread
.sleep(usecs(1));
279 assert(sw
.peek() > stopped
);
282 @safe nothrow @nogc unittest
284 assert(StopWatch
.init
.peek() == Duration
.zero
);
289 Sets the total time which the StopWatch has been running (i.e. what peek
292 The StopWatch does not have to be stopped for setTimeElapsed to be
293 called, nor will calling it cause the StopWatch to stop.
295 void setTimeElapsed(Duration timeElapsed
) @safe nothrow @nogc
297 enum hnsecsPerSecond
= convert
!("seconds", "hnsecs")(1);
298 _ticksElapsed
= convClockFreq(timeElapsed
.total
!"hnsecs", hnsecsPerSecond
, MonoTime
.ticksPerSecond
);
299 _timeStarted
= MonoTime
.currTime
;
303 @system nothrow @nogc unittest
305 import core
.thread
: Thread
;
308 sw
.setTimeElapsed(hours(1));
310 // As discussed in MonoTime's documentation, converting between
311 // Duration and ticks is not exact, though it will be close.
312 // How exact it is depends on the frequency/resolution of the
313 // system's monotonic clock.
314 assert(abs(sw
.peek() - hours(1)) < usecs(1));
317 Thread
.sleep(usecs(1));
318 assert(sw
.peek() > hours(1) + usecs(1));
323 Returns whether this StopWatch is currently running.
325 @property bool running() @safe const pure nothrow @nogc
331 @safe nothrow @nogc unittest
344 // We track the ticks for the elapsed time rather than a Duration so that we
345 // don't lose any precision.
347 bool _running
= false; // Whether the StopWatch is currently running
348 MonoTime _timeStarted
; // The time the StopWatch started measuring (i.e. when it was started or reset).
349 long _ticksElapsed
; // Total time that the StopWatch ran before it was stopped last.
354 Benchmarks code for speed assessment and comparison.
357 fun = aliases of callable objects (e.g. function names). Each callable
358 object should take no arguments.
359 n = The number of times each function is to be executed.
362 The amount of time (as a $(REF Duration,core,time)) that it took to call
363 each function $(D n) times. The first value is the length of time that
364 it took to call $(D fun[0]) $(D n) times. The second value is the length
365 of time it took to call $(D fun[1]) $(D n) times. Etc.
367 Duration
[fun
.length
] benchmark(fun
...)(uint n
)
369 Duration
[fun
.length
] result
;
370 auto sw
= StopWatch(AutoStart
.yes
);
372 foreach (i
, unused
; fun
)
377 result
[i
] = sw
.peek();
386 import std
.conv
: to
;
390 void f1() { auto b
= a
; }
391 void f2() { auto b
= to
!string(a
); }
392 auto r
= benchmark
!(f0
, f1
, f2
)(10_000);
393 Duration f0Result
= r
[0]; // time f0 took to run 10,000 times
394 Duration f1Result
= r
[1]; // time f1 took to run 10,000 times
395 Duration f2Result
= r
[2]; // time f2 took to run 10,000 times
398 @safe nothrow unittest
400 import std
.conv
: to
;
404 void f1() nothrow { auto b
= to
!string(a
); }
405 auto r
= benchmark
!(f0
, f1
)(1000);
407 assert(r
[0] >= Duration
.zero
);
409 assert(r
[0] > Duration
.zero
);
410 assert(r
[1] > Duration
.zero
);
412 assert(r
[0] < seconds(1));
413 assert(r
[1] < seconds(1));
416 @safe nothrow @nogc unittest
421 void f0() nothrow @nogc { ++f0Count
; }
422 void f1() nothrow @nogc { ++f1Count
; }
423 void f2() nothrow @nogc { ++f2Count
; }
424 auto r
= benchmark
!(f0
, f1
, f2
)(552);
425 assert(f0Count
== 552);
426 assert(f1Count
== 552);
427 assert(f2Count
== 552);