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 // CLOCK_MONOTONIC and clock_gettime will be added to core.sys.posix.time in 21 // tangort v1.7.0, see tangort issue #6. 22 import core.sys.posix.time; // clockid_t, timespec 23 24 version (UnitTest) 25 { 26 import ocean.core.Test; 27 } 28 29 extern (C) private 30 { 31 enum: clockid_t 32 { 33 CLOCK_MONOTONIC = 1 34 } 35 36 int clock_gettime(clockid_t clk_id, timespec* t); 37 } 38 39 /******************************************************************************* 40 41 Timer for measuring small intervals, such as the duration of a 42 subroutine or other reasonably small period. 43 --- 44 StopWatch elapsed; 45 46 elapsed.start; 47 48 // do something 49 // ... 50 51 double i = elapsed.stop; 52 ulong us = elapsed.microsec; 53 --- 54 55 The measured interval is either an integer value in units of 56 microseconds -- returned by `microsec` -- or a floating-point value in 57 units of seconds with fractions -- returned by `stop`. 58 The integer value has always a precision of 1µs and is recommended if 59 you want to compare it with reference values such as checking if it's 60 below or above 1ms (e.g. `elapsed.microsec <= 1000`). 61 Although floating-point representation seems often more convenient, bear 62 in mind that 63 - The precision is relative to the value (i.e. the greater the values 64 the lower the absolute precision). 65 - Comparing fractions can yield unexpected results due to rounding and 66 because decimal fractions have no exact binary floating-point 67 representation. To avoid surprises like this using the integer 68 represenation of time spans is in general recommended. 69 70 There is some minor overhead in using StopWatch, so take that into 71 account 72 73 *******************************************************************************/ 74 75 public struct StopWatch 76 { 77 // TODO: From tangort v1.7.0 import clock_gettime and CLOCK_MONOTONIC. 78 import core.sys.posix.time: timespec; 79 80 import ocean.core.ExceptionDefinitions : PlatformException; 81 82 83 private ulong started; 84 private enum double multiplier = 1.0 / 1_000_000.0; 85 86 /*********************************************************************** 87 88 Start the timer 89 90 ***********************************************************************/ 91 92 void start () 93 { 94 started = timer; 95 } 96 97 /*********************************************************************** 98 99 Return elapsed duration (in seconds) since start() 100 101 ***********************************************************************/ 102 103 double sec () 104 { 105 return multiplier * (&this).microsec; 106 } 107 108 /*********************************************************************** 109 110 Return elapsed time since the last start() as microseconds 111 112 ***********************************************************************/ 113 114 ulong microsec () 115 { 116 return (timer >= started)? (timer - started) : 0; 117 } 118 119 /*********************************************************************** 120 121 Return the current time as an Interval 122 123 ***********************************************************************/ 124 125 private static ulong timer () 126 { 127 timespec t; 128 if (clock_gettime(CLOCK_MONOTONIC, &t)) 129 throw new PlatformException ("Timer :: CLOCK_MONOTONIC is not available"); 130 131 return t.tv_sec * 1_000_000UL + t.tv_nsec / 1_000UL; 132 } 133 } 134 135 unittest 136 { 137 StopWatch t; 138 t.start; 139 140 auto wait_time = timespec(0, 10_000); // 10_000 ns = 10 microsec 141 142 // validate that stopwatch time counts up 143 nanosleep(&wait_time, null); 144 auto stopwatch_sec = t.sec; 145 test!(">")(stopwatch_sec, 0); 146 147 // validate that checking the elapsed time doesn't stop the timer 148 nanosleep(&wait_time, null); 149 auto stopwatch_sec_again = t.sec; 150 test!(">")(stopwatch_sec_again, stopwatch_sec); 151 }