Monotonic time in Mac OS X

January 19th, 2009

Turns out calculating elapsed times in Mac OS X is a pain. Why not just use gettimeofday(2)? If you’re trying to calculate a time period between two events, you should be able to call gettimeofday(2) before and after, subtract the results, and you’d have an elapsed time, right? Well, almost – but gettimeofday(2) is based on the “wall clock”, so if the time changes between calls to gettimeofday(2) – e.g. daylight savings starts, leap seconds get added, etc – then the elapsed time will be wrong. This also leads to fun when the time is adjusted backwards between calls.

Enter the concept of monotonic time. Monotonic time is a time that increases, well, monotonically from an arbitrary reference point. It is not based on the wall clock, so it’s pretty useless for saying “this event happened at this point in time”, but it is very useful for calculating the time difference between two events, as the clock source won’t ever change between reads.

POSIX defines a nice way of getting times from arbitrary clock sources – clock_gettime(2). You can ask clock_gettime(2) for a time from a number of different clocks. For example, CLOCK_REALTIME will give you the wall time. Linux implements a number of clock sources, including the CLOCK_MONOTONIC source which gives you a nice monotonic time to use for measuring time differences reliably.

Unfortunately Mac OS X (at least 10.5) doesn’t implement clock_gettime(2), which means there’s no simple (read standard) way to get a monotonic time on Mac OS X. As always though, there is a solution.

<mach/mach_time.h> exports the function mach_absolute_time(). This function returns a CPU dependent time, which can, after a bit of magic, be used as a monotonic time. There is a good discussion on the Apple message boards (which you’ll have to follow through to get the whole story) and a Technical Note which are interesting references. Unfortunately it’s not mentioned in any man pages though.

As it turns out, the absolute time units returned by mach_absolute_time() are not affected by CPU power saving, sleeping, etc. mach_absolute_time() is the base function from which all higher level timer related functions are implemented, for example, gettimeofday(2). So, how do we use mach_absolute_time() as a monotonic clock? Well, calling mach_absolute_time() before and after the event we want to measure is a start. Then we need to convert the difference to something useful. The return value from mach_absolute_time() can be converted to a more useful time unit by multiplying it by the fraction returned by mach_timebase_info(). This fraction will convert the absolute time into a time in nanoseconds. Armed with this, we can write some code that converts a difference into the familiar struct timespec:

#include <mach/mach_time.h>
#include <time.h>
#include <stdio.h>

void mach_absolute_difference(uint64_t end, uint64_t start, struct timespec *tp) {
        uint64_t difference = end - start;
        static mach_timebase_info_data_t info = {0,0};

        if (info.denom == 0)
                mach_timebase_info(&info);

        uint64_t elapsednano = difference * (info.numer / info.denom);

        tp->tv_sec = elapsednano * 1e-9;
        tp->tv_nsec = elapsednano - (tp->tv_sec * 1e9);
}

int main(int argc, char *argv[]) {
        uint64_t start,end;
        struct timespec tp;
        start = mach_absolute_time();
        sleep(1);
        end = mach_absolute_time();
        mach_absolute_difference(end, start, &tp);
        printf("%lu seconds, %lu nanoseconds\n", tp.tv_sec, tp.tv_nsec);
        return 0;
}

Running this code produces the output:

kenshin:~ scottr$ make time
cc     time.c   -o time
kenshin:~ scottr$ ./time
1 seconds, 130951 nanoseconds

The fraction returned by mach_timebase_info() does not appear to change as the processor scales, at least on this Intel mac :) I’ve done some rudimentary tests to make sure that time does increase monotonically when the system clock changes, when the machine goes to sleep, etc, and it all seems to work fine. If anyone out there with a Mac wants to test these theories please do and please get back to me with your results.

It’s a pity that Apple didn’t decide to implement clock_gettime(2) themselves. I guess we can hope that in a later version of Mac OS X it will be implemented but until then we apear to be stuck with this workaround.

Entry Filed under: General

11 Comments Add your own

  • 1. Rick Meier  |  March 4th, 2009 at 1:33 pm

    Unfortunately this is not quite monotonic. If the machine goes to sleep during the call you will find that the difference is incorrect. You have to detect sleep and wake notifications and maintain those yourself. See http://lists.apple.com/archives/darwin-dev/2008/Oct/msg00073.html

  • 2. Nate  |  April 16th, 2009 at 5:02 pm

    Thanks for the writeup… was looking for the clock_gettime myself but your workaround is satisfactory for a quick test.

    Just a note: for the mach_absolute_difference() function, the tp arg should be a pointer, not address. Works great otherwise

  • 3. Björn  |  June 3rd, 2009 at 11:09 am

    Thanks for this blog entry!
    Small correction: s/usec/nsec/

  • 4. Scott  |  June 3rd, 2009 at 1:08 pm

    Thanks, both changes made.

  • 5. Lucas  |  June 11th, 2009 at 10:29 pm

    Great snippet, thanks!

  • 6. Teemu Kurppa  |  August 4th, 2009 at 7:14 pm

    Thanks Scott for an excellent writeup. I just stumbled on this problem and was mystified.

  • 7. mmw  |  November 1st, 2009 at 8:13 am

    The clock_get_time function returns the current time kept by a clock. The value returned is a monotonically increasing value (unless tampered with via the clock_set_time function).

    CLOCK_REALTIME // work around
    gettimeofday (&tv, NULL)
    TIMEVAL_TO_TIMESPEC(&tv, tp);

    case CLOCK_MONOTONIC:
    case CLOCK_PROCESS_CPUTIME_ID: // getpid yield
    case CLOCK_THREAD_CPUTIME_ID: // current thread yield
    if (KERN_SUCCESS == (k_ret = host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &m_clock))) {
    f (KERN_SUCCESS == (k_ret = clock_get_time(m_clock, &m_time))) {

  • 8. mmw  |  November 1st, 2009 at 9:18 am

    a functional workaround could be written as following:

    http://le-depotoir.googlecode.com/svn/trunk/misc/clock_gettime_stub.c

    it’s not perfect, but so far better than your workaround.

  • 9. krelian  |  April 17th, 2010 at 9:31 pm

    mmw,

    RDTSC isn’t monotonic on multi-core systems. Modern OSes, including OS X 10.5 and up (Intel) use HPET.

  • 10. mmw  |  April 20th, 2010 at 4:18 pm

    yep that,s but writing a workaround userspace even if it does not do the 100% rigth job can be written in a nice way not in smemling durty style Btw I made some tests by reading some comments and mostly people are lying and are wrong In deed even it does reflect the reality that,s not so far I bchmarked on fb versud macos this code use only a lucky trick due the nature of mach task BTW this issue can be only solved by commiting a libc patch including a private syscall to the kernel the rigth to deal this that moreover the posix definition is incomplete looking how people interpreted the poor description on various os including linux their implementation can be easly argued

  • 11. mmw  |  April 20th, 2010 at 4:43 pm

    sorry I sent that from my ugly iphone, that’s true those results can be only an average, and you cannot get the reality but something close, my code is using a tricky lucky trick due the nature of a mach task and how the translation back is made to a BSD* thread, then reading some comments it makes me laugh,

    by the way the only place where you could fix that is in the kernel where you are aware of each processus

    but if people are taking reference on a linux box, they just get results which are flattering their ego, doing benchmarks on huge data set my linux box is lying so much that’s a shame.

Leave a Comment

hidden

Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe to the comments via RSS Feed


Calendar

January 2009
M T W T F S S
« Aug   Feb »
 1234
567891011
12131415161718
19202122232425
262728293031  

Most Recent Posts