Monotonic time

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 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.

Precise and reliable time measurements.

Process.clock_gettime(Process::CLOCK_MONOTONIC)

# or get milliseconds
Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)

# or microseconds
Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_microsecond)

if you're on Linux 2.6.28+ or macOS 10.12+, you can also use:

Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW)

It has three more digits precision.

> Process.clock_gettime(Process::CLOCK_MONOTONIC)
=> 102771.161163

> Process.clock_gettime(Process::CLOCK_MONOTONIC_RAW)
=> 102771.161163125

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).

java.lang.System.nanoTime()

You probably dont need a gem but 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 is a fast, high resolution timer library for recording performance metrics. The time measurements returned in nanoseconds.

MonotonicTime wraps hitimes to return measurements in seconds: MonotonicTime.

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

You could stubs or allow Monotonic module:

# Minitest
Monotonic.stubs(:now).returns(t)

# RSpec
allow(Monotonic).to receive(:now) { t }

See this Pull Request for example: stripe/stripe-ruby#857.

You cant compare monotonic time across processes or machines.

The origin (zero) of the returned value varies. For example, system start up time, process start up time, the Epoch, etc.
https://rubyapi.org/3.1/o/process#method-c-clock_gettime

Because different processes / machines started at different time. Monotonic time is only comparable across calls in a process, not across processes or machines.

Cheers,
Juanito