web-dev-qa-db-fra.com

Option clock_gettime sous Mac OS X

Lors de la compilation d'un programme que j'ai écrit sur Mac OS X après avoir installé les bibliothèques nécessaires via MacPorts, le message d'erreur suivant s'affiche:

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

Il semble que clock_gettime n'est pas implémenté sous Mac OS X. Existe-t-il un autre moyen d'obtenir le Epoch time in nanosecondes? Malheureusement, gettimeofday est dans microsecondes.

65
Delan Azabani

En effet, il semble ne pas avoir été implémenté pour macOS avant Sierra 10.12. Vous voudrez peut-être regarder ceci entrée de blog , mais cela ne semble plus être disponible. L'idée principale est dans l'extrait de code suivant:

#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;
}
32
Jens Gustedt

Après des heures passées à parcourir différentes réponses, blogs et en-têtes, j'ai trouvé un moyen portable d'obtenir l'heure actuelle:

#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

ou consultez ce Gist: https://Gist.github.com/1087739

J'espère que cela fait gagner du temps à quelqu'un. À votre santé!

127
jbenet

Aucune des solutions ci-dessus ne répond à la question. Soit ils ne vous donnent pas le temps absolu Unix, soit leur précision est de 1 microseconde. La solution la plus populaire de jbenet est lente (~ 6000ns) et ne compte pas en nanosecondes même si son retour le laisse supposer. Vous trouverez ci-dessous un test de 2 solutions proposées par jbenet et Dmitri B, ainsi que mon point de vue. Vous pouvez exécuter le code sans modifications.

La 3ème solution compte en nanosecondes et vous donne un temps absolu Unix relativement rapide (~ 90ns). Donc, si quelqu'un trouve cela utile, veuillez nous le faire savoir tous ici :-). Je m'en tiendrai à celle de Dmitri B (solution n ° 1 dans le code) - elle répond mieux à mes besoins.

J'avais besoin d'alternative de qualité commerciale à clock_gettime () pour passer des appels pthread_… chronométrés… et j'ai trouvé cette discussion très utile. Merci les gars.

/*
 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;
}
34
Sergey D
#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
30
Dmitri Bouianov

Tout ce dont vous avez besoin est décrit dans Questions techniques QA1398: Questions techniques QA1398: Unités de temps absolues pour Mach , la fonction que vous souhaitez utiliser est fondamentalement mach_absolute_time.

Voici une version légèrement antérieure de l'exemple de code de cette page qui fait tout en utilisant des appels Mach (la version actuelle utilise AbsoluteToNanoseconds de CoreServices). Sous OS X actuel (c'est-à-dire sur Snow Leopard sous x86_64), les valeurs de temps absolues sont en nanosecondes et ne nécessitent donc aucune conversion. Donc, si vous êtes bon et que vous écrivez du code portable, vous allez convertir, mais si vous faites quelque chose de rapide et de sale pour vous-même, vous n'avez pas besoin de vous déranger.

FWIW, mach_absolute_time est vraiment rapide.

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;
}
21
Charphacy

Notez que macOS Sierra 10.12 prend désormais en charge clock_gettime ():

#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);
}

Il fournit des nanosecondes; Cependant, la résolution est de 1000, elle est donc (in) effectivement limitée à des microsecondes:

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

Vous aurez besoin de XCode 8 ou version ultérieure pour pouvoir utiliser cette fonctionnalité. Le code compilé pour utiliser cette fonctionnalité ne fonctionnera pas sur les versions de Mac OS X (10.11 ou antérieures).

13
James Wald

Merci pour vos messages

Je pense que vous pouvez ajouter les lignes suivantes

#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

Faites-moi savoir ce que vous obtenez pour la latence et la granularité

9
ilciavo

Maristic a la meilleure réponse à ce jour. Permettez-moi de simplifier et d'ajouter une remarque. #include Et Init():

#include <mach/mach_time.h>

double conversion_factor;

void Init() {
  mach_timebase_info_data_t timebase;
  mach_timebase_info(&timebase);
  conversion_factor = (double)timebase.numer / (double)timebase.denom;
}

Utilisé comme:

  uint64_t t1, t2;

  Init();

  t1 = mach_absolute_time();
  /* profiled code here */
  t2 = mach_absolute_time();

  double duration_ns = (double)(t2 - t1) * conversion_factor;  

Cette minuterie a une latence de 65ns +/- 2ns (CPU à 2 GHz). Utilisez ceci si vous avez besoin d'une "évolution temporelle" d'une exécution unique Sinon, bouclez votre code 10000 Fois et profilez même avec gettimeofday(), qui est portable (POSIX) et a la latence de 100ns +/- 0.5ns (Bien que seulement 1us granularité).

4
P Marecki

J'ai essayé la version avec clock_get_time et mis en cache l'appel Host_get_clock_service. C'est beaucoup plus lent que gettimeofday, il prend plusieurs microsecondes par invocation. Et, pire encore, la valeur de retour a un pas de 1 000, c’est-à-dire qu’il s’agit toujours d’une granularité de l'ordre de la microseconde.

Je conseillerais d'utiliser gettimeofday et de multiplier tv_usec par 1000.

3
Bernd Paysan

Basé sur l'open source mach_absolute_time.c nous pouvons voir que la ligne extern mach_port_t clock_port; nous dit qu’un port mach est déjà initialisé pour le temps monotone. Ce port d’horloge est accessible directement sans avoir à appeler mach_absolute_time puis convertissant en arrière en un struct timespec. Contourner un appel à mach_absolute_time devrait améliorer les performances.

J'ai créé un petit rapport Github (PosixMachTiming) avec le code basé sur l'extern clock_port et a fil similaire . PosixMachTiming émule clock_gettime pour CLOCK_REALTIME et CLOCK_MONOTONIC. Il émule également la fonction clock_nanosleep pour le temps absolu monotone. S'il vous plaît essayer et voir comment la performance se compare. Vous voudrez peut-être créer des tests comparatifs ou émuler d’autres horloges/fonctions POSIX?

2
ChisholmKyle

Dès au moins aussi loin que Mountain Lion, mach_absolute_time() renvoie nanosecondes et pas le temps absolu (qui correspond au nombre de cycles de bus).

Le code suivant sur mon MacBook Pro (Core i7 à 2 GHz) indiquait que le temps pour appeler mach_absolute_time() était en moyenne de 39 ns sur 10 cycles (min. 35, max. 45), ce qui correspond en gros au temps écoulé entre le retour du deux appels à mach_absolute_time (), environ 1 invocation:

#include <stdint.h>
#include <mach/mach_time.h>
#include <iostream>

using namespace std;

int main()
{
   uint64_t now, then;
   uint64_t abs;

   then = mach_absolute_time(); // return nanoseconds
   now = mach_absolute_time();
   abs = now - then;

   cout << "nanoseconds = " << abs << endl;
}
1
Jon Spencer

J'ai trouvé une autre solution portable.

Déclarez dans un fichier d’en-tête (ou même dans votre fichier source):

/* If compiled on DARWIN/Apple platforms. */
#ifdef DARWIN
#define    CLOCK_REALTIME    0x2d4e1588
#define    CLOCK_MONOTONIC   0x0
#endif /* DARWIN */

Et l'ajout de l'implémentation de la fonction:

#ifdef DARWIN

/*
 * Bellow we provide an alternative for clock_gettime,
 * which is not implemented in Mac OS X.
 */
static inline int clock_gettime(int clock_id, struct timespec *ts)
{
    struct timeval tv;

    if (clock_id != CLOCK_REALTIME) 
    {
        errno = EINVAL;
        return -1;
    }
    if (gettimeofday(&tv, NULL) < 0) 
    {
        return -1;
    }
    ts->tv_sec = tv.tv_sec;
    ts->tv_nsec = tv.tv_usec * 1000;
    return 0;
}

#endif /* DARWIN */

N'oubliez pas d'inclure <time.h>.

0
user4108694
void clock_get_uptime(uint64_t *result);

void clock_get_system_microtime(            uint32_t *secs,
                                            uint32_t *microsecs);

void clock_get_system_nanotime(             uint32_t *secs,
                                            uint32_t *nanosecs);
void clock_get_calendar_microtime(          uint32_t *secs,
                                            uint32_t *microsecs);

void clock_get_calendar_nanotime(           uint32_t *secs,
                                            uint32_t *nanosecs);

Pour MacOS, vous pouvez trouver une bonne information sur la page de leurs développeurs https://developer.Apple.com/library/content/documentation/Darwin/Conceptual/KernelProgramming/services/services.html

0
Alex Paniutin