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.
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;
}
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é!
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(µ, 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(µ, 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;
}
#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
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;
}
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).
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é
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é).
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.
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?
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;
}
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>
.
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