4

clock_gettime(CLOCK_MONOTONIC, ...) is available in Linux but not OS X. The Mach timers are available in OS X but not in Linux.

How can I get a ns-precision monotonic clock in C that works both on Linux and OS X?

Douglas B. Staple
  • 10,510
  • 8
  • 31
  • 58
  • This is motivated by [this other question and answer](http://stackoverflow.com/questions/12392278/measure-time-in-linux-getrusage-vs-clock-gettime-vs-clock-vs-gettimeofday/12480485#12480485). – Douglas B. Staple Feb 09 '14 at 22:03
  • 1
    Important reminder: While the Linux monotonic clock is promised to indeed be monotonic (which is important if you need stability on a machine where the user or NTP client may reset the clock), and more precise, it is not actually guaranteed to be more accurate. That depends on the details of the OS build and the hardware it's running on. In general the resolution will also be better, but may not be equal to the precision; some of the low bits may be noise, just as some of the low bits of the msec clock may be noise. If that matters, check how your platform has defined it. – keshlam Feb 10 '14 at 04:32
  • @keshlam no, on Linux all other clocks derive from the monotonic clock, as can be seen in the kernel source – mirabilos Oct 05 '22 at 13:06

2 Answers2

9
/* 
This is based on the snippet current_utc_time.c from:
https://gist.github.com/jbenet/1087739

On OS X, compile with: gcc get_monotonic_time.c
   Linux, compile with: gcc get_monotonic_time.c -lrt
*/

#include <time.h>
#include <sys/time.h>
#include <stdio.h>

#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif

// Use clock_gettime in linux, clock_get_time in OS X.
void get_monotonic_time(struct timespec *ts){
#ifdef __MACH__
  clock_serv_t cclock;
  mach_timespec_t mts;
  host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
  clock_get_time(cclock, &mts);
  mach_port_deallocate(mach_task_self(), cclock);
  ts->tv_sec = mts.tv_sec;
  ts->tv_nsec = mts.tv_nsec;
#else
  clock_gettime(CLOCK_MONOTONIC, ts);
#endif
}

double get_elapsed_time(struct timespec *before, struct timespec *after){
  double deltat_s  = after->tv_sec - before->tv_sec;
  double deltat_ns = after->tv_nsec - before->tv_nsec;
  return deltat_s + deltat_ns*1e-9;
}

int main(){

  // Do something and time how long it takes.
  struct timespec before, after;
  get_monotonic_time(&before);
  double sum=0.;
  unsigned u;
  for(u=1; u<100000000; u++)
    sum += 1./u/u;
  get_monotonic_time(&after);
  printf("sum = %e\n", sum);
  printf("deltaT = %e s\n", get_elapsed_time(&before,&after));

}
Douglas B. Staple
  • 10,510
  • 8
  • 31
  • 58
  • At a guess, if you're using a high-precision timer, you are probably concerned about accuracy or performance. Neither of which are helped by looking up a mach service each time you want the time and then releasing the port again. Do this just once! – marko Feb 09 '14 at 22:10
  • @marko Yes, that's true, if one is calling these functions often. In my situation I only want to call these timing functions a few times before and after some expensive function calls, so it's not an issue. – Douglas B. Staple Feb 09 '14 at 23:08
  • It seems that you could use [`mach_absolute_time`](http://opensource.apple.com/source/Libc/Libc-262/i386/mach/mach_absolute_time.c) instead of the calls to `host_get_clock_service`, `clock_get_time`, and `mach_port_deallocate`. – JWWalker Aug 14 '15 at 23:44
0

I used the answer by Douglas (accepted answer), his references and other examples floating around the internet (such as this question).

This answer is to include my version of the code which emulates the clock_gettime for CLOCK_REALTIME and CLOCK_MONOTONIC. It also emulates the function clock_nanosleep() for absolute monotonic time. The code is hosted on GitHub here.

For it to work, the only additional bit needed in your code is

#ifdef __MACH__
timing_mach_init();
#endif

then use clock_gettime() and clock_nanosleep_abstime() as if you were using a system that is actually POSIX compliant (with realtime extensions).

Community
  • 1
  • 1
ChisholmKyle
  • 449
  • 3
  • 10
  • If you want to clean things up and no longer need the timer, make a function in the source file which calls `mach_port_deallocate(mach_host_self(), timing_mach_g.cclock)` – ChisholmKyle Jul 28 '15 at 22:40