1 /*******************************************************************************
2 
3         Copyright:
4             Copyright (c) 2007 Kris Bell.
5             Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
6             All rights reserved.
7 
8         License:
9             Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
10             See LICENSE_TANGO.txt for details.
11 
12         Version: Feb 2007: Initial release
13 
14         Authors: Kris
15 
16 *******************************************************************************/
17 
18 module ocean.time.StopWatch;
19 
20 import core.sys.posix.time;
21 
22 version (unittest)
23 {
24     import ocean.core.Test;
25 }
26 
27 /*******************************************************************************
28 
29         Timer for measuring small intervals, such as the duration of a
30         subroutine or other reasonably small period.
31         ---
32         StopWatch elapsed;
33 
34         elapsed.start;
35 
36         // do something
37         // ...
38 
39         double i = elapsed.stop;
40         ulong us = elapsed.microsec;
41         ---
42 
43         The measured interval is either an integer value in units of
44         microseconds -- returned by `microsec` -- or a floating-point value in
45         units of seconds with fractions -- returned by `stop`.
46         The integer value has always a precision of 1µs and is recommended if
47         you want to compare it with reference values such as checking if it's
48         below or above 1ms (e.g. `elapsed.microsec <= 1000`).
49         Although floating-point representation seems often more convenient, bear
50         in mind that
51           - The precision is relative to the value (i.e. the greater the values
52             the lower the absolute precision).
53           - Comparing fractions can yield unexpected results due to rounding and
54             because decimal fractions have no exact binary floating-point
55             representation. To avoid surprises like this using the integer
56             represenation of time spans is in general recommended.
57 
58         There is some minor overhead in using StopWatch, so take that into
59         account
60 
61 *******************************************************************************/
62 
63 public struct StopWatch
64 {
65         import ocean.core.ExceptionDefinitions : PlatformException;
66 
67 
68         private ulong  started;
69         private enum double multiplier = 1.0 / 1_000_000.0;
70 
71         /***********************************************************************
72 
73                 Start the timer
74 
75         ***********************************************************************/
76 
77         void start ()
78         {
79                 started = timer;
80         }
81 
82         /***********************************************************************
83 
84                 Return elapsed duration (in seconds) since start()
85 
86         ***********************************************************************/
87 
88         double sec ()
89         {
90                 return multiplier * this.microsec;
91         }
92 
93         /***********************************************************************
94 
95                 Return elapsed time since the last start() as microseconds
96 
97         ***********************************************************************/
98 
99         ulong microsec ()
100         {
101                  return (timer >= started)? (timer - started) : 0;
102         }
103 
104         /***********************************************************************
105 
106                 Return the current time as an Interval
107 
108         ***********************************************************************/
109 
110         private static ulong timer ()
111         {
112                 timespec t;
113                 if (clock_gettime(CLOCK_MONOTONIC, &t))
114                     throw new PlatformException ("Timer :: CLOCK_MONOTONIC is not available");
115 
116                 return t.tv_sec * 1_000_000UL + t.tv_nsec / 1_000UL;
117         }
118 }
119 
120 unittest
121 {
122     StopWatch t;
123     t.start;
124 
125     auto wait_time = timespec(0, 10_000);  // 10_000 ns = 10 microsec
126 
127     // validate that stopwatch time counts up
128     nanosleep(&wait_time, null);
129     auto stopwatch_sec = t.sec;
130     test!(">")(stopwatch_sec, 0);
131 
132     // validate that checking the elapsed time doesn't stop the timer
133     nanosleep(&wait_time, null);
134     auto stopwatch_sec_again = t.sec;
135     test!(">")(stopwatch_sec_again, stopwatch_sec);
136 }