alternativa clock_gettime en Mac OS X

Al comstackr un progtwig que escribí en Mac OS X después de instalar las bibliotecas necesarias a través de MacPorts, aparece este error:

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

Parece que clock_gettime no está implementado en Mac OS X. ¿Hay alguna forma alternativa de obtener el tiempo de época en nanosegundos ? Lamentablemente gettimeofday está en microsegundos .

En efecto, parece que no se implementa para macOS antes de Sierra 10.12. Es posible que desee ver esta entrada de blog , pero parece que ya no está disponible. La idea principal está en el siguiente fragmento de código:

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

Después de horas de leer detenidamente diferentes respuestas, blogs y encabezados, encontré una forma portátil de obtener la hora actual:

 #include  #include  #ifdef __MACH__ #include  #include  #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 

o mira esta idea: https://gist.github.com/1087739

Espero que esto le ahorre tiempo a alguien. ¡Aclamaciones!

Ninguna de las soluciones anteriores responde la pregunta. O no le dan tiempo absoluto de Unix, o su precisión es de 1 microsegundo. La solución más popular de jbenet es lenta (~ 6000ns) y no cuenta en nanosegundos, aunque su retorno así lo sugiere. A continuación hay una prueba para 2 soluciones sugeridas por jbenet y Dmitri B, más mi opinión sobre esto. Puede ejecutar el código sin cambios.

La tercera solución cuenta en nanosegundos y le da un tiempo absoluto de Unix razonablemente rápido (~ 90ns). Entonces, si alguien lo encuentra útil, por favor, háganos saber aquí :-). Me apegaré al de Dmitri B (solución # 1 en el código) – se ajusta mejor a mis necesidades.

Necesitaba una alternativa comercial de calidad para clock_gettime () para hacer llamadas pthread_ … timed .. y encontré esta discusión muy útil. Gracias chicos.

 /* 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  #include  #include  #include  /* gettimeofday */ #include  /* mach_absolute_time */ #include  /* host_get_clock_service, mach_... */ #include  /* 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; } 
 #if defined(__MACH__) && !defined(CLOCK_REALTIME) #include  #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 

Todo lo que necesita se describe en la sección de Preguntas y respuestas técnicas QA1398: Q & A técnica QA1398: Unidades de tiempo absoluto de Mach , básicamente la función que desea es mach_absolute_time .

Aquí hay una versión ligeramente anterior del código de muestra de esa página que hace todo usando llamadas Mach (la versión actual usa AbsoluteToNanoseconds desde CoreServices). En el OS X actual (es decir, en Snow Leopard en x86_64), los valores de tiempo absolutos son en realidad nanosegundos y, por lo tanto, no requieren ninguna conversión. Entonces, si eres bueno y escribes código portátil, convertirás, pero si solo estás haciendo algo rápido y sucio por ti mismo, no necesitas molestarte.

FWIW, mach_absolute_time es realmente rápido.

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

Tenga en cuenta que macOS Sierra 10.12 ahora es compatible con clock_gettime ():

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

Proporciona nanosegundos; sin embargo, la resolución es 1000, por lo que está (en) efectivamente limitada a microsegundos:

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

Necesitará XCode 8 o posterior para poder usar esta característica. El código comstackdo para usar esta característica no se ejecutará en las versiones de Mac OS X (10.11 o anterior).

Gracias por tus publicaciones

Creo que puedes agregar las siguientes líneas

 #ifdef __MACH__ #include  #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  #endif 

Déjame saber qué obtienes por latencia y granularidad

Maristic tiene la mejor respuesta aquí hasta la fecha. Permítanme simplificar y agregar un comentario. #include e Init() :

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

Usar como:

  uint64_t t1, t2; Init(); t1 = mach_absolute_time(); /* profiled code here */ t2 = mach_absolute_time(); double duration_ns = (double)(t2 - t1) * conversion_factor; 

Tal temporizador tiene una latencia de 65ns +/- 2ns (CPU de 65ns +/- 2ns ). Úselo si necesita “evolución temporal” de ejecución única. De lo contrario, itere su código 10000 veces y perfil incluso con gettimeofday() , que es portátil (POSIX), y tiene la latencia de 100ns +/- 0.5ns (aunque solo 1us granularidad).

Intenté la versión con clock_get_time, y guardé en caché la llamada host_get_clock_service. Es mucho más lento que gettimeofday, lleva varios microsegundos por invocación. Y, lo que es peor, el valor de retorno tiene pasos de 1000, es decir, sigue siendo una granularidad de microsegundos.

Aconsejo usar gettimeofday y multiplicar tv_usec por 1000.

Basado en el código abierto mach_absolute_time.c podemos ver que la línea extern mach_port_t clock_port; nos dice que hay un puerto mach ya inicializado para el tiempo monotónico. Se puede acceder directamente a este puerto de reloj sin tener que recurrir a llamar a mach_absolute_time luego a volver a una struct timespec . Omitir una llamada a mach_absolute_time debería mejorar el rendimiento.

Creé un pequeño repository de Github (PosixMachTiming) con el código basado en el clock_port externo y un hilo similar . PosixMachTiming emula clock_gettime para CLOCK_REALTIME y CLOCK_MONOTONIC . También emula la función clock_nanosleep por tiempo monotónico absoluto. Por favor, pruébalo y observa cómo se compara el rendimiento. ¿Tal vez desee crear pruebas comparativas o emular otros relojes / funciones POSIX?

Desde por lo menos desde Mountain Lion, mach_absolute_time() devuelve nanosegundos y no tiempo absoluto (que era el número de ciclos de bus).

El siguiente código en mi MacBook Pro (2 GHz Core i7) mostró que el tiempo para llamar a mach_absolute_time() promedió 39 ns durante 10 carreras (min 35, max 45), que es básicamente el tiempo entre el regreso de las dos llamadas a mach_absolute_time (), aproximadamente 1 invocación:

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

Encontré otra solución portátil.

Declare en algún archivo de encabezado (o incluso en su fuente):

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

Y agregue la implementación de la función:

 #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 */ 

No te olvides de incluir .

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

Para MacOS puede encontrar una buena información en su página de desarrolladores https://developer.apple.com/library/content/documentation/Darwin/Conceptual/KernelProgramming/services/services.html