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 }