System Clock
The operation system comes with many clocks, real time (system-wide clock), monotonic, boot time, process cpu time, thread cpu time. When we do Time.now
, it retrives time from the System clock (Note: System clock measures real (i.e., wall-clock) time).
System clock changes regularly:
This clock is affected by discontinuous jumps in the system time
(e.g., if the system administrator manually changes the
clock), and by the incremental adjustments performed by
adjtime and NTP.See CLOCK_REALTIME
So we shouldn't rely on system clock to do any task involves time measurement. Good news is operation system comes with another clock: Monotonic clock.
Monotonic Clock
Monotonic Clock cannot be set and represents monotonic time since some unspecified starting point. Monotonic Clock is not affected by discontinuous jumps in the system time (e.g., if the system administrator manually changes the clock), but STILL affected by adjtime(3) and NTP.
Usage of Monotonic time
Precise and reliable time measurements.
- connection_pool uses Monotonic time to timeout when connection is not available
- Monotonic time being used widely in Concurrent Ruby
Use Monotonic time in CRuby
Process.clock_gettime(Process::CLOCK_MONOTONIC)
if you're on Linux 2.6.28+ or macOS 10.12+, you can also use:
Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW)
It’s similar to CLOCK_MONOTONIC
, but provides access to a raw hardware-based time, that is not subject to NTP adjustments or the incremental adjustments performed by adjtime(3)
.
For more information, please see Ruby’s Process.clock_gettime docs and clock_getres(2)
.
Use Monotonic time in JRuby
java.lang.System.nanoTime()
What gems to use?
There is a Monotonic gem, but it is not using Monotonic time and it doesn't work for JRuby.
However, if you're looking for full feature native implementation across all platforms, checkout Hitimes!
HiTimes
Hitimes is a fast, high resolution timer library for recording performance metrics. The time measurements returned in nanoseconds.
MonotonicTime
MonotonicTime wraps hitimes
to return measurements in seconds: MonotonicTime.
Drop-in module
But I think you wouldn’t want to introduce a dependency in your project. You can use this pure ruby drop-in module in your application, it’s based on concurrent-ruby’s implementation that works for CRuby and JRuby.
Monotonic.now # returns monotonic time in *seconds*
Monotonic.time do
1+1 # measure time elapsed for expression 1+1
end
Testing
You could stubs
or allow
Monotonic module:
# Minitest
Monotonic.stubs(:now).returns(t)
# RSpec
allow(Monotonic).to receive(:now) { t }
Replace Timecop with Monotonic Time
See this Pull Request for example: stripe/stripe-ruby#857.
Cheers,
Juanito