Linux Clock_Gettime(Clock_Monotonic) Strange Non-Monotonic Behavior

Linux clock_gettime(CLOCK_MONOTONIC) strange non-monotonic behavior

man clock_gettime says:

CLOCK_MONOTONIC_RAW (since Linux 2.6.28; Linux-specific)

Similar to CLOCK_MONOTONIC, but provides access to a raw hardware-based time that is not subject to NTP adjustments.

Since CLOCK_MONOTONIC_RAW is not subject of NTP adjustments, I guess CLOCK_MONOTONIC could be.

We had similar problems with Redhat Enterprise 5.0 with 2.6.18 kernel and some specific Itanium processor. We couldn't reproduce it with other processor on the same OS. It was fixed in RHEL 5.3 with slightly newer kernel and some Redhat patches.

In what cases CLOCK_MONOTONIC might not be available

Is it reasonable to assume that CLOCK_MONOTONIC clock is available on all modern Linux servers?

Yes. It is reasonable to assume that.

From the wording of the gettime manual entry, we can infer that really old versions of glibc do not support CLOCK_MONOTONIC. (I am still trying to figure out how old ... but probably when glibc claimed POSIX 1003.1 compliance.)

CLOCK_MONOTONIC was specified (at least) in IEEE Std 1003.1, 2004 Edition, though it remains possible for a compliant libc implementation to not support CLOCK_MONOTONIC.

The Linux kernel source code has had support for a CLOCK_MONOTONIC clock at least since Linux 3.0 (2011).

From other sources, it also depends on how your system's glibc was built. (When built with "emulated timers", CLOCK_MONOTONIC is not supported.)

Here are some cases where it is not supported:

  • There can be problems with some old CPU chips; e.g. https://bugzilla.redhat.com/show_bug.cgi?id=1499480

  • It is not supported for some (all?) versions of Cygwin.

  • It is not supported for some (all?) versions of uCLibc on ARM.


It is also possible for CLOCK_MONOTONIC to be supported but buggy:

  • Linux clock_gettime(CLOCK_MONOTONIC) strange non-monotonic behavior

clock_gettime() still not monotonic - alternatives?

This has nothing to do with the mythology of clock_gettime's monotonic clock not actually being monotonic (which probably has a basis in reality, but which was never well documented and probably fixed a long time ago). It's just a bug in your program. tv_nsec is the nanoseconds portion of a time value that's stored as two fields:

  • tv_sec - whole seconds
  • tv_nsec - nanoseconds in the range 0 to 999999999

Of course tv_nsec is going to jump backwards from 999999999 to 0 when tv_sec increments. To compute differences of timespec structs, you need to take 1000000000 times the difference in seconds and add that to the difference in nanoseconds. Of course this could quickly overflow if you don't convert to a 64-bit type first.

What is the difference between CLOCK_MONOTONIC & CLOCK_MONOTONIC_RAW?

CLOCK_MONOTONIC never experiences discontinuities due to NTP time adjustments, but it does change in frequency as NTP learns what error exists between the local oscillator and the upstream servers.

CLOCK_MONOTONIC_RAW is simply the local oscillator, not disciplined by NTP. This could be very useful if you want to implement some other time synchronization algorithm against a clock which is not fighting you due to NTP. While ntpd (the reference implementation of NTP protocol and the most widespread NTP daemon) is reputed to be "gentle" with time adjustments, it's more accurate to say it's gentle with the absolute time. It's willing to slew the clock by 500ppm which is pretty dramatic if you're in a position to measure your clock frequency against some other standard.

The utility of CLOCK_MONOTONIC_RAW is going to be limited until facilities like pthread_timedwait_monotonic offer an option to use that timebase.

clock_gettime returns some extrange values

The fluctuation is caused by the OS scheduler. It pauses your process and gives a chance to an other process to run.

Can you report back results with CLOCK_PROCESS_CPUTIME_ID?

Is CLOCK_MONOTONIC process (or thread) specific?

The answer Maxim and comments to that answered the second part of your question, I believe. To expand on the answer for the first part, POSIX 2008 states


If the Monotonic Clock option is supported, all implementations shall support a clock_id of CLOCK_MONOTONIC defined in <time.h>. This clock represents the monotonic clock for the system. For this clock, the value returned by clock_gettime() represents the amount of time (in seconds and nanoseconds) since an unspecified point in the past (for example, system start-up time, or the Epoch). This point does not change after system start-up time.

In particular, note "the monotonic clock for the system". That is, per-system and not per-process, it keeps ticking even though your process is not running. Also, "This point does not change after system start-up time.", which again implies that it keeps ticking regardless of whether a particular process is running or sleeping.

So, either you have found a bug in the Linux implementation, or more likely, in your test program.

Measuring Elapsed Time Using clock_gettime(CLOCK_MONOTONIC)

Write a helper function that calculates the difference between two timespecs:

int64_t difftimespec_ns(const struct timespec after, const struct timespec before)
{
return ((int64_t)after.tv_sec - (int64_t)before.tv_sec) * (int64_t)1000000000
+ ((int64_t)after.tv_nsec - (int64_t)before.tv_nsec);
}

If you want it in microseconds, just divide it by 1000, or use:

int64_t difftimespec_us(const struct timespec after, const struct timespec before)
{
return ((int64_t)after.tv_sec - (int64_t)before.tv_sec) * (int64_t)1000000
+ ((int64_t)after.tv_nsec - (int64_t)before.tv_nsec) / 1000;
}

Remember to include <inttypes.h>, so that you can use conversion "%" PRIi64 to print integers of int64_t type:

    printf("%09" PRIi64 " | 5\n", difftimespec_ns(after, before));

How do I get monotonic time durations in python?

That function is simple enough that you can use ctypes to access it:

#!/usr/bin/env python

__all__ = ["monotonic_time"]

import ctypes, os

CLOCK_MONOTONIC_RAW = 4 # see <linux/time.h>

class timespec(ctypes.Structure):
_fields_ = [
('tv_sec', ctypes.c_long),
('tv_nsec', ctypes.c_long)
]

librt = ctypes.CDLL('librt.so.1', use_errno=True)
clock_gettime = librt.clock_gettime
clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]

def monotonic_time():
t = timespec()
if clock_gettime(CLOCK_MONOTONIC_RAW , ctypes.pointer(t)) != 0:
errno_ = ctypes.get_errno()
raise OSError(errno_, os.strerror(errno_))
return t.tv_sec + t.tv_nsec * 1e-9

if __name__ == "__main__":
print monotonic_time()


Related Topics



Leave a reply



Submit