monotimes.nim 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2019 Nim contributors
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ##[
  10. The ``std/monotimes`` module implements monotonic timestamps. A monotonic
  11. timestamp represents the time that has passed since some system defined
  12. point in time. The monotonic timestamps are guaranteed to always increase,
  13. meaning that that the following is guaranteed to work:
  14. .. code-block:: nim
  15. let a = getMonoTime()
  16. # ... do some work
  17. let b = getMonoTime()
  18. assert a <= b
  19. This is not guaranteed for the `times.Time` type! This means that the
  20. `MonoTime` should be used when measuring durations of time with
  21. high precision.
  22. However, since `MonoTime` represents the time that has passed since some
  23. unknown time origin, it cannot be converted to a human readable timestamp.
  24. If this is required, the `times.Time` type should be used instead.
  25. The `MonoTime` type stores the timestamp in nanosecond resolution, but note
  26. that the actual supported time resolution differs for different systems.
  27. See also
  28. ========
  29. * `times module <times.html>`_
  30. ]##
  31. import times
  32. type
  33. MonoTime* = object ## Represents a monotonic timestamp.
  34. ticks: int64
  35. when defined(macosx):
  36. type
  37. MachTimebaseInfoData {.pure, final, importc: "mach_timebase_info_data_t",
  38. header: "<mach/mach_time.h>".} = object
  39. numer, denom: int32
  40. proc mach_absolute_time(): int64 {.importc, header: "<mach/mach.h>".}
  41. proc mach_timebase_info(info: var MachTimebaseInfoData) {.importc,
  42. header: "<mach/mach_time.h>".}
  43. let machAbsoluteTimeFreq = block:
  44. var freq: MachTimebaseInfoData
  45. mach_timebase_info(freq)
  46. freq
  47. when defined(js):
  48. proc getJsTicks: float =
  49. ## Returns ticks in the unit seconds
  50. {.emit: """
  51. var isNode = typeof module !== 'undefined' && module.exports
  52. if (isNode) {
  53. var process = require('process');
  54. var time = process.hrtime()
  55. return time[0] + time[1] / 1000000000;
  56. } else {
  57. return window.performance.now() / 1000;
  58. }
  59. """.}
  60. # Workaround for #6752.
  61. {.push overflowChecks: off.}
  62. proc `-`(a, b: int64): int64 =
  63. system.`-`(a, b)
  64. proc `+`(a, b: int64): int64 =
  65. system.`+`(a, b)
  66. {.pop.}
  67. elif defined(posix):
  68. import posix
  69. elif defined(windows):
  70. proc QueryPerformanceCounter(res: var uint64) {.
  71. importc: "QueryPerformanceCounter", stdcall, dynlib: "kernel32".}
  72. proc QueryPerformanceFrequency(res: var uint64) {.
  73. importc: "QueryPerformanceFrequency", stdcall, dynlib: "kernel32".}
  74. let queryPerformanceCounterFreq = block:
  75. var freq: uint64
  76. QueryPerformanceFrequency(freq)
  77. 1_000_000_000'u64 div freq
  78. proc getMonoTime*(): MonoTime {.tags: [TimeEffect].} =
  79. ## Get the current `MonoTime` timestamp.
  80. ##
  81. ## When compiled with the JS backend and executed in a browser,
  82. ## this proc calls `window.performance.now()`, which is not supported by
  83. ## older browsers. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now)
  84. ## for more information.
  85. when defined(JS):
  86. let ticks = getJsTicks()
  87. result = MonoTime(ticks: (ticks * 1_000_000_000).int64)
  88. elif defined(macosx):
  89. let ticks = mach_absolute_time()
  90. result = MonoTime(ticks: ticks * machAbsoluteTimeFreq.numer div
  91. machAbsoluteTimeFreq.denom)
  92. elif defined(posix):
  93. var ts: Timespec
  94. discard clock_gettime(CLOCK_MONOTONIC, ts)
  95. result = MonoTime(ticks: ts.tv_sec.int64 * 1_000_000_000 +
  96. ts.tv_nsec.int64)
  97. elif defined(windows):
  98. var ticks: uint64
  99. QueryPerformanceCounter(ticks)
  100. result = MonoTime(ticks: (ticks * queryPerformanceCounterFreq).int64)
  101. proc ticks*(t: MonoTime): int64 =
  102. ## Returns the raw ticks value from a `MonoTime`. This value always uses
  103. ## nanosecond time resolution.
  104. t.ticks
  105. proc `$`*(t: MonoTime): string =
  106. $t.ticks
  107. proc `-`*(a, b: MonoTime): Duration =
  108. ## Returns the difference between two `MonoTime` timestamps as a `Duration`.
  109. initDuration(nanoseconds = (a.ticks - b.ticks))
  110. proc `+`*(a: MonoTime, b: Duration): MonoTime =
  111. ## Increases `a` by `b`.
  112. MonoTime(ticks: a.ticks + b.inNanoseconds)
  113. proc `-`*(a: MonoTime, b: Duration): MonoTime =
  114. ## Reduces `a` by `b`.
  115. MonoTime(ticks: a.ticks - b.inNanoseconds)
  116. proc `<`*(a, b: MonoTime): bool =
  117. ## Returns true if `a` happened before `b`.
  118. a.ticks < b.ticks
  119. proc `<=`*(a, b: MonoTime): bool =
  120. ## Returns true if `a` happened before `b` or if they happened simultaneous.
  121. a.ticks <= b.ticks
  122. proc `==`*(a, b: MonoTime): bool =
  123. ## Returns true if `a` and `b` happened simultaneous.
  124. a.ticks == b.ticks
  125. proc high*(typ: typedesc[MonoTime]): MonoTime =
  126. ## Returns the highest representable `MonoTime`.
  127. MonoTime(ticks: high(int64))
  128. proc low*(typ: typedesc[MonoTime]): MonoTime =
  129. ## Returns the lowest representable `MonoTime`.
  130. MonoTime(ticks: low(int64))
  131. when isMainModule:
  132. let d = initDuration(nanoseconds = 10)
  133. let t1 = getMonoTime()
  134. let t2 = t1 + d
  135. doAssert t2 - t1 == d
  136. doAssert t1 == t1
  137. doAssert t1 != t2
  138. doAssert t2 - d == t1
  139. doAssert t1 < t2
  140. doAssert t1 <= t2
  141. doAssert t1 <= t1
  142. doAssert not(t2 < t1)
  143. doAssert t1 < high(MonoTime)
  144. doAssert low(MonoTime) < t1