clock_gettime-Alternative in Mac OS X

Lesezeit: 13 Minuten

Benutzeravatar von Delan Azabani
Delan Azabani

Beim Kompilieren eines Programms, das ich unter Mac OS X geschrieben habe, nachdem ich die erforderlichen Bibliotheken über MacPorts installiert habe, erhalte ich diesen Fehler:

In function 'nanotime':
error: 'CLOCK_REALTIME' undeclared (first use in this function)
error: (Each undeclared identifier is reported only once
error: for each function it appears in.)

Anscheinend clock_gettime ist in Mac OS X nicht implementiert. Gibt es eine alternative Möglichkeit, die Epoche Zeit in Nanosekunden? Leider gettimeofday ist in Mikrosekunden.

  • Meine Dokumentation sagt “Alle Implementierungen unterstützen die systemweite Echtzeituhr, die durch CLOCK_REALTIME identifiziert wird.” Hast du #include <time.h>?

    – pmg

    2. März 2011 um 12:18 Uhr

  • Das hat nicht geholfen. clock_gettime ist in Mac OS X nicht implementiert.

    – Delan Azabani

    2. März 2011 um 12:23 Uhr

  • Ich weiss. Es ist in meiner Linker-Befehlszeile. Ich komme überhaupt nicht bis zur Verknüpfungsphase. Mac OS X hat das nicht clock_gettimewährend Linux dies tut.

    – Delan Azabani

    2. März 2011 um 12:26 Uhr


  • Ich habe dazu einen kurzen Wrapper geschrieben: gist.github.com/alfwatt/3588c5aa1f7a1ef7a3bb

    – alfwatt

    4. August 2015 um 21:11 Uhr

  • Beachten Sie, dass macOS Sierra 10.12 (September 2016, XCode 8) und höher unterstützt wird clock_gettime() direkt – wie in dieser Antwort von James Wald erwähnt.

    – Jonathan Leffler

    26. Oktober 2016 um 4:32 Uhr


Benutzeravatar von jbenet
jbenet

Nachdem ich stundenlang verschiedene Antworten, Blogs und Header durchgesehen hatte, fand ich einen tragbaren Weg, um die aktuelle Uhrzeit abzurufen:

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

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



struct timespec ts;

#ifdef __MACH__ // OS X does not have clock_gettime, use clock_get_time
clock_serv_t cclock;
mach_timespec_t mts;
host_get_clock_service(mach_host_self(), CALENDAR_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_REALTIME, &ts);
#endif

oder sehen Sie sich diese Zusammenfassung an: https://gist.github.com/1087739

Hoffe, das spart jemand Zeit. Prost!

  • ist host_get_clock_service teuer? Würde es sich auszahlen, es für den Prozess zwischenzuspeichern? ist es wiederverwendbar? Thread sicher? Vielen Dank – 🙂

    – Peterk

    15. Oktober 2011 um 1:56 Uhr


  • benötigt außerdem include: #ifdef __MACH__ #include #include #endif

    – Nikolai Wjahi

    31. Januar 2012 um 22:24 Uhr


  • @NikolayVyahhi Ja! Ich habe sie im Kern. Da Sie sie jedoch nicht gefunden haben, ist es vielleicht am besten, sie der Antwort hinzuzufügen.

    – jbenet

    1. Februar 2012 um 19:09 Uhr

  • Hat vielleicht jemand das obige zeitlich gemessen __MACH__ Code? Mit einem unabhängigen Mikrosekunden-Timer (gut getestet) habe ich den Eindruck, dass zwei Aufrufe des obigen Codes ~ 25 Mikrosekunden kosten.

    – P. Marecki

    12. Juli 2012 um 14:14 Uhr


  • Weiß jemand die Antwort auf die Frage von @peterk . In meinen Tests scheint es, als würde das Zwischenspeichern die Leistung verdoppeln, aber muss ich es pro Thread zwischenspeichern, oder kann ich einen erstellen und ihn für alle meine Threads freigeben?

    – robbie_c

    17. März 2014 um 18:18 Uhr


Benutzeravatar von Sergey D
Sergej D

Keine der oben genannten Lösungen beantwortet die Frage. Entweder geben sie Ihnen keine absolute Unix-Zeit oder ihre Genauigkeit beträgt 1 Mikrosekunde. Die beliebteste Lösung von jbenet ist langsam (~6000 ns) und zählt nicht in Nanosekunden, obwohl ihre Rückkehr dies vermuten lässt. Unten ist ein Test für 2 Lösungen, die von jbenet und Dmitri B vorgeschlagen wurden, plus meine Meinung dazu. Sie können den Code ohne Änderungen ausführen.

Die dritte Lösung zählt in Nanosekunden und gibt Ihnen relativ schnell absolute Unix-Zeit (~ 90 ns). Also, wenn jemand es nützlich findet – bitte lassen Sie es uns alle hier wissen :-). Ich bleibe bei der von Dmitri B (Lösung Nr. 1 im Code) – sie passt besser zu meinen Bedürfnissen.

Ich brauchte eine Alternative in kommerzieller Qualität zu clock_gettime(), um pthread_…timed..-Aufrufe zu machen, und fand diese Diskussion sehr hilfreich. Danke Leute.

/*
 Ratings of alternatives to clock_gettime() to use with pthread timed waits:
    Solution 1 "gettimeofday":
        Complexity      : simple
        Portability     : POSIX 1
        timespec        : easy to convert from timeval to timespec
        granularity     : 1000 ns,
        call            : 120 ns,
        Rating          : the best.

    Solution 2 "host_get_clock_service, clock_get_time":
        Complexity      : simple (error handling?)
        Portability     : Mac specific (is it always available?)
        timespec        : yes (struct timespec return)
        granularity     : 1000 ns (don't be fooled by timespec format)
        call time       : 6000 ns
        Rating          : the worst.

    Solution 3 "mach_absolute_time + gettimeofday once":
        Complexity      : simple..average (requires initialisation)
        Portability     : Mac specific. Always available
        timespec        : system clock can be converted to timespec without float-math
        granularity     : 1 ns.
        call time       : 90 ns unoptimised.
        Rating          : not bad, but do we really need nanoseconds timeout?

 References:
 - OS X is UNIX System 3 [U03] certified
    http://www.opengroup.org/homepage-items/c987.html

 - UNIX System 3 <--> POSIX 1 <--> IEEE Std 1003.1-1988
    http://en.wikipedia.org/wiki/POSIX
    http://www.unix.org/version3/

 - gettimeofday() is mandatory on U03,
   clock_..() functions are optional on U03,
   clock_..() are part of POSIX Realtime extensions
    http://www.unix.org/version3/inttables.pdf

 - clock_gettime() is not available on MacMini OS X
    (Xcode > Preferences > Downloads > Command Line Tools = Installed)

 - OS X recommends to use gettimeofday to calculate values for timespec
    https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/pthread_cond_timedwait.3.html

 - timeval holds microseconds, timespec - nanoseconds
    http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html

 - microtime() is used by kernel to implement gettimeofday()
    http://ftp.tw.freebsd.org/pub/branches/7.0-stable/src/sys/kern/kern_time.c

 - mach_absolute_time() is really fast
    http://www.opensource.apple.com/source/Libc/Libc-320.1.3/i386/mach/mach_absolute_time.c

 - Only 9 deciaml digits have meaning when int nanoseconds converted to double seconds
    Tutorial: Performance and Time post uses .12 precision for nanoseconds
    http://www.macresearch.org/tutorial_performance_and_time

 Example:
    Three ways to prepare absolute time 1500 milliseconds in the future to use with pthread timed functions.

 Output, N = 3, stock MacMini, OSX 10.7.5, 2.3GHz i5, 2GB 1333MHz DDR3:
    inittime.tv_sec = 1390659993
    inittime.tv_nsec = 361539000
    initclock = 76672695144136
    get_abs_future_time_0() : 1390659994.861599000
    get_abs_future_time_0() : 1390659994.861599000
    get_abs_future_time_0() : 1390659994.861599000
    get_abs_future_time_1() : 1390659994.861618000
    get_abs_future_time_1() : 1390659994.861634000
    get_abs_future_time_1() : 1390659994.861642000
    get_abs_future_time_2() : 1390659994.861643671
    get_abs_future_time_2() : 1390659994.861643877
    get_abs_future_time_2() : 1390659994.861643972
 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>       /* gettimeofday */
#include <mach/mach_time.h> /* mach_absolute_time */
#include <mach/mach.h>      /* host_get_clock_service, mach_... */
#include <mach/clock.h>     /* clock_get_time */

#define BILLION 1000000000L
#define MILLION 1000000L

#define NORMALISE_TIMESPEC( ts, uint_milli )            \
    do {                                                \
        ts.tv_sec += uint_milli / 1000u;                \
        ts.tv_nsec += (uint_milli % 1000u) * MILLION;   \
        ts.tv_sec += ts.tv_nsec / BILLION;              \
        ts.tv_nsec = ts.tv_nsec % BILLION;              \
    } while (0)

static mach_timebase_info_data_t timebase = { 0, 0 }; /* numer = 0, denom = 0 */
static struct timespec           inittime = { 0, 0 }; /* nanoseconds since 1-Jan-1970 to init() */
static uint64_t                  initclock;           /* ticks since boot to init() */

void init()
{
    struct timeval  micro;      /* microseconds since 1 Jan 1970 */

    if (mach_timebase_info(&timebase) != 0)
        abort();                            /* very unlikely error */

    if (gettimeofday(&micro, NULL) != 0)
        abort();                            /* very unlikely error */

    initclock = mach_absolute_time();

    inittime.tv_sec = micro.tv_sec;
    inittime.tv_nsec = micro.tv_usec * 1000;
    printf("\tinittime.tv_sec = %ld\n", inittime.tv_sec);
    printf("\tinittime.tv_nsec = %ld\n", inittime.tv_nsec);
    printf("\tinitclock = %ld\n", (long)initclock);
}

/*
 * Get absolute future time for pthread timed calls
 *  Solution 1: microseconds granularity
 */
struct timespec get_abs_future_time_coarse(unsigned milli)
{
    struct timespec future;         /* ns since 1 Jan 1970 to 1500 ms in the future */
    struct timeval  micro = {0, 0}; /* 1 Jan 1970 */

    (void) gettimeofday(&micro, NULL);
    future.tv_sec = micro.tv_sec;
    future.tv_nsec = micro.tv_usec * 1000;
    NORMALISE_TIMESPEC( future, milli );
    return future;
}

/*
 * Solution 2: via clock service
 */
struct timespec get_abs_future_time_served(unsigned milli)
{
    struct timespec     future;
    clock_serv_t        cclock;
    mach_timespec_t     mts;

    host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
    clock_get_time(cclock, &mts);
    mach_port_deallocate(mach_task_self(), cclock);
    future.tv_sec = mts.tv_sec;
    future.tv_nsec = mts.tv_nsec;
    NORMALISE_TIMESPEC( future, milli );
    return future;
}

/*
 * Solution 3: nanosecond granularity
 */
struct timespec get_abs_future_time_fine(unsigned milli)
{
    struct timespec future;     /* ns since 1 Jan 1970 to 1500 ms in future */
    uint64_t        clock;      /* ticks since init */
    uint64_t        nano;       /* nanoseconds since init */

    clock = mach_absolute_time() - initclock;
    nano = clock * (uint64_t)timebase.numer / (uint64_t)timebase.denom;
    future = inittime;
    future.tv_sec += nano / BILLION;
    future.tv_nsec += nano % BILLION;
    NORMALISE_TIMESPEC( future, milli );
    return future;
}

#define N 3

int main()
{
    int                 i, j;
    struct timespec     time[3][N];
    struct timespec   (*get_abs_future_time[])(unsigned milli) =
    {
        &get_abs_future_time_coarse,
        &get_abs_future_time_served,
        &get_abs_future_time_fine
    };

    init();
    for (j = 0; j < 3; j++)
        for (i = 0; i < N; i++)
            time[j][i] = get_abs_future_time[j](1500);  /* now() + 1500 ms */

    for (j = 0; j < 3; j++)
        for (i = 0; i < N; i++)
            printf("get_abs_future_time_%d() : %10ld.%09ld\n",
                   j, time[j][i].tv_sec, time[j][i].tv_nsec);

    return 0;
}

Benutzeravatar von Jens Gustedt
Jens Gustedt

Tatsächlich scheint es für macOS nicht vor Sierra 10.12 implementiert zu sein. Vielleicht möchten Sie sich das ansehen Blog-Eintrag. Die Hauptidee ist im folgenden Code-Snippet enthalten:

#include <mach/mach_time.h>
#define ORWL_NANO (+1.0E-9)
#define ORWL_GIGA UINT64_C(1000000000)

static double orwl_timebase = 0.0;
static uint64_t orwl_timestart = 0;

struct timespec orwl_gettime(void) {
  // be more careful in a multithreaded environement
  if (!orwl_timestart) {
    mach_timebase_info_data_t tb = { 0 };
    mach_timebase_info(&tb);
    orwl_timebase = tb.numer;
    orwl_timebase /= tb.denom;
    orwl_timestart = mach_absolute_time();
  }
  struct timespec t;
  double diff = (mach_absolute_time() - orwl_timestart) * orwl_timebase;
  t.tv_sec = diff * ORWL_NANO;
  t.tv_nsec = diff - (t.tv_sec * ORWL_GIGA);
  return t;
}

  • Ich möchte jedoch keine monotone Zeit, ich möchte die Echtzeit seit der Epoche in Nanosekunden.

    – Delan Azabani

    2. März 2011 um 12:35 Uhr

  • @Delan, ich verstehe nicht, warum Sie das wollen würden, das ist nutzlose Präzision für etwas, das in der Größenordnung von Jahren zählt. Normalerweise benötigen Sie Nanosekunden, um eine Funktion oder so zu timen. Dann reicht es aus, sich die Zeit davor und danach zu nehmen, wie es in diesem Blog gemacht wird. Aber man könnte das immer simulieren, indem man nimmt gettimeofday und mach_absolute_time am Anfang Ihres Programms und addieren Sie dann Dinge.

    – Jens Gustedt

    2. März 2011 um 12:58 Uhr

  • Verwechseln Sie niemals Monotonie und Echtzeit. Die Echtzeit kann springen, wenn ein NTP-Daemon die Systemuhr korrigiert. Das sind wirklich zwei völlig verschiedene Dinge.

    – Mäuse

    3. Mai 2012 um 18:08 Uhr

  • “Blogeintrag” ist jetzt kaputt 🙁

    – P. Marecki

    12. Juli 2012 um 15:07 Uhr

  • Verwenden Sie stattdessen diesen Link: web.archive.org/web/20100517095152/http://www.wand.net.nz/…

    – Brian Cannard

    13. August 2014 um 16:54 Uhr

Benutzeravatar von Dmitri Bouianov
Dmitri Bouianov

#if defined(__MACH__) && !defined(CLOCK_REALTIME)
#include <sys/time.h>
#define CLOCK_REALTIME 0
// clock_gettime is not implemented on older versions of OS X (< 10.12).
// If implemented, CLOCK_REALTIME will have already been defined.
int clock_gettime(int /*clk_id*/, struct timespec* t) {
    struct timeval now;
    int rv = gettimeofday(&now, NULL);
    if (rv) return rv;
    t->tv_sec  = now.tv_sec;
    t->tv_nsec = now.tv_usec * 1000;
    return 0;
}
#endif

Alles, was Sie brauchen, ist in beschrieben Technische Fragen und Antworten QA1398: Technische Fragen und Antworten QA1398: Absolute Zeiteinheiten in Machim Grunde ist die gewünschte Funktion mach_absolute_time.

Hier ist eine etwas frühere Version des Beispielcodes von dieser Seite, die alles mit Mach-Aufrufen macht (die aktuelle Version verwendet AbsoluteToNanoseconds von CoreServices). Im aktuellen OS X (dh auf Snow Leopard auf x86_64) sind die absoluten Zeitwerte tatsächlich in Nanosekunden und erfordern daher überhaupt keine Konvertierung. Wenn Sie also gut sind und portablen Code schreiben, werden Sie konvertieren, aber wenn Sie nur schnell etwas für sich selbst tun, müssen Sie sich nicht darum kümmern.

FWIW, mach_absolute_time ist Ja wirklich schnell.

uint64_t GetPIDTimeInNanoseconds(void)
{
    uint64_t        start;
    uint64_t        end;
    uint64_t        elapsed;
    uint64_t        elapsedNano;
    static mach_timebase_info_data_t    sTimebaseInfo;

    // Start the clock.

    start = mach_absolute_time();

    // Call getpid. This will produce inaccurate results because 
    // we're only making a single system call. For more accurate 
    // results you should call getpid multiple times and average 
    // the results.

    (void) getpid();

    // Stop the clock.

    end = mach_absolute_time();

    // Calculate the duration.

    elapsed = end - start;

    // Convert to nanoseconds.

    // If this is the first time we've run, get the timebase.
    // We can use denom == 0 to indicate that sTimebaseInfo is 
    // uninitialised because it makes no sense to have a zero 
    // denominator is a fraction.

    if ( sTimebaseInfo.denom == 0 ) {
        (void) mach_timebase_info(&sTimebaseInfo);
    }

    // Do the maths. We hope that the multiplication doesn't 
    // overflow; the price you pay for working in fixed point.

    elapsedNano = elapsed * sTimebaseInfo.numer / sTimebaseInfo.denom;

    printf("multiplier %u / %u\n", sTimebaseInfo.numer, sTimebaseInfo.denom);
    return elapsedNano;
}

Benutzeravatar von Jonathan Leffler
Jonathan Leffler

Beachten Sie, dass macOS Sierra 10.12 jetzt clock_gettime() unterstützt:

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

int main() {
    struct timespec res;
    struct timespec time;

    clock_getres(CLOCK_REALTIME, &res);
    clock_gettime(CLOCK_REALTIME, &time);

    printf("CLOCK_REALTIME: res.tv_sec=%lu res.tv_nsec=%lu\n", res.tv_sec, res.tv_nsec);
    printf("CLOCK_REALTIME: time.tv_sec=%lu time.tv_nsec=%lu\n", time.tv_sec, time.tv_nsec);
}

Es liefert Nanosekunden; Die Auflösung beträgt jedoch 1000, sodass sie (un)effektiv auf Mikrosekunden begrenzt ist:

CLOCK_REALTIME: res.tv_sec=0 res.tv_nsec=1000
CLOCK_REALTIME: time.tv_sec=1475279260 time.tv_nsec=525627000

Sie benötigen XCode 8 oder höher, um diese Funktion nutzen zu können. Code, der zur Verwendung dieser Funktion kompiliert wurde, kann nicht auf Versionen von Mac OS X (10.11 oder früher) ausgeführt werden.

Danke für deine Beiträge

Ich denke, Sie können die folgenden Zeilen hinzufügen

#ifdef __MACH__
#include <mach/mach_time.h>
#define CLOCK_REALTIME 0
#define CLOCK_MONOTONIC 0
int clock_gettime(int clk_id, struct timespec *t){
    mach_timebase_info_data_t timebase;
    mach_timebase_info(&timebase);
    uint64_t time;
    time = mach_absolute_time();
    double nseconds = ((double)time * (double)timebase.numer)/((double)timebase.denom);
    double seconds = ((double)time * (double)timebase.numer)/((double)timebase.denom * 1e9);
    t->tv_sec = seconds;
    t->tv_nsec = nseconds;
    return 0;
}
#else
#include <time.h>
#endif

Lassen Sie mich wissen, was Sie für Latenz und Granularität erhalten

  • Es wäre besser, wenn Sie die zwischenspeichern würden mach_timebase_info aufrufen (vielleicht mit einer statischen Variable, um es sauber zu halten). mach_timebase_info() ist ein Syscall und dauert auf meinem Rechner ~180ns. Im Gegensatz zu den ~22ns für mach_absolute_time()was im Grunde nur ein Sampling ist rdtsc.

    – Aqtau

    15. September 2014 um 8:16 Uhr

1416450cookie-checkclock_gettime-Alternative in Mac OS X

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy