usar tiempo programa poner nanosegundos milisegundos medir medidor ejecucion dev como calcular c portability time-precision

programa - ¿Cómo medir el tiempo en milisegundos usando ANSI C?



tiempo de ejecucion dev c (8)

En ventanas:

SYSTEMTIME t; GetLocalTime(&t); swprintf_s(buff, L"[%02d:%02d:%02d:%d]/t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);

Usando solo ANSI C, ¿hay alguna manera de medir el tiempo con milisegundos de precisión o más? Estaba navegando por time.h pero solo encontré funciones de segunda precisión.


La mejor precisión que puede obtener es mediante el uso de la instrucción "rdtsc" x86-only, que puede proporcionar una resolución de reloj (por supuesto, debe tener en cuenta el costo de la llamada rdtsc, que se puede medir fácilmente en inicio de la aplicación).

La captura principal aquí es medir la cantidad de relojes por segundo, lo que no debería ser demasiado difícil.


La respuesta aceptada es suficiente. Pero mi solución es más simple. Solo pruebo en Linux, uso gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.

tv_sec use gettimeofday , tv_sec es la parte de segundo, y tv_usec es microsegundos , no milisegundos .

long currentTimeMillis() { struct timeval time; gettimeofday(&time, NULL); return time.tv_sec * 1000 + time.tv_usec / 1000; } int main() { printf("%ld/n", currentTimeMillis()); // wait 1 second sleep(1); printf("%ld/n", currentTimeMillis()); return 0; }

Imprime:

1522139691342 1522139692342 , exactamente un segundo.


Siempre uso la función clock_gettime (), devolviendo la hora desde el reloj CLOCK_MONOTONIC. El tiempo devuelto es la cantidad de tiempo, en segundos y nanosegundos, desde algún punto no especificado en el pasado, como el inicio del sistema de la época.

#include <stdio.h> #include <stdint.h> #include <time.h> int64_t timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p) { return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) - ((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec); } int main(int argc, char **argv) { struct timespec start, end; clock_gettime(CLOCK_MONOTONIC, &start); // Some code I am interested in measuring clock_gettime(CLOCK_MONOTONIC, &end); uint64_t timeElapsed = timespecDiff(&end, &start); }


timespec_get from C11 devuelve hasta nanosegundos, redondeados a la resolución de la implementación.

Parece una estafa ANSI de POSIX '' clock_gettime .

Ejemplo: una printf se realiza cada 100 ms en Ubuntu 15.10:

#include <stdio.h> #include <stdlib.h> #include <time.h> static long get_nanos(void) { struct timespec ts; timespec_get(&ts, TIME_UTC); return (long)ts.tv_sec * 1000000000L + ts.tv_nsec; } int main(void) { long nanos; long last_nanos; long start; nanos = get_nanos(); last_nanos = nanos; start = nanos; while (1) { nanos = get_nanos(); if (nanos - last_nanos > 100000000L) { printf("current nanos: %ld/n", nanos - start); last_nanos = nanos; } } return EXIT_SUCCESS; }

El borrador estándar de C11 N1570 7.27.2.5 La función timespec_get dice:

Si base es TIME_UTC, el miembro tv_sec se establece en el número de segundos desde una época definida de implementación, truncada en un valor completo y el miembro tv_nsec se establece en el número integral de nanosegundos, redondeado a la resolución del reloj del sistema. (321)

321) Aunque un objeto struct timespec describe los tiempos con una resolución de nanosegundos, la resolución disponible depende del sistema e incluso puede ser mayor de 1 segundo.

C ++ 11 también tiene std::chrono::high_resolution_clock : C ++ multiplataforma de alta resolución Timer

glibc 2.21 lo implementa en sysdeps/posix/timespec_get.c como:

int timespec_get (struct timespec *ts, int base) { switch (base) { case TIME_UTC: if (__clock_gettime (CLOCK_REALTIME, ts) < 0) return 0; break; default: return 0; } return base; }

tan claramente:

  • solo TIME_UTC es actualmente compatible

  • reenvía a __clock_gettime (CLOCK_REALTIME, ts) , que es una API POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html

    Linux x86-64 tiene una clock_gettime sistema clock_gettime .

    Tenga en cuenta que este no es un método de micro-benchmarking a prueba de fallas porque:

    • man clock_gettime dice que esta medida puede tener discontinuidades si cambia alguna configuración de tiempo del sistema mientras se ejecuta su programa. Este debería ser un evento raro, por supuesto, y es posible que puedas ignorarlo.

    • esto mide el tiempo de pared, por lo que si el planificador decide olvidarse de su tarea, parecerá que se ejecuta por más tiempo.

    Por esas razones, getrusage() podría ser una mejor herramienta de evaluación comparativa de POSIX, a pesar de su precisión máxima de microsegundos más baja.

    Más información en: ¿ Medir el tiempo en Linux - hora vs reloj vs getrusage vs clock_gettime vs gettimeofday vs timespec_get?


Implementando una solución portátil

Como ya se mencionó aquí que no existe una solución ANSI adecuada con suficiente precisión para el problema de medición de tiempo, quiero escribir sobre las formas de obtener una solución portátil y, si es posible, una solución de medición de tiempo de alta resolución.

Reloj monotónico vs. sellos de tiempo

En términos generales, hay dos formas de medir el tiempo:

  • reloj monotónico;
  • sello de tiempo actual (fecha).

El primero utiliza un contador de reloj monotónico (a veces se llama contador de tic) que cuenta ticks con una frecuencia predefinida, por lo que si tiene un valor de ticks y se conoce la frecuencia, puede convertir ticks fácilmente en tiempo transcurrido. En realidad, no está garantizado que un reloj monotónico refleje la hora actual del sistema de ninguna manera, también puede contar tics desde el inicio de un sistema. Pero garantiza que siempre se ejecute un reloj de manera creciente independientemente del estado del sistema. Por lo general, la frecuencia está ligada a una fuente de alta resolución de hardware, por eso proporciona una alta precisión (depende del hardware, pero la mayoría del hardware moderno no tiene problemas con las fuentes de reloj de alta resolución).

La segunda forma proporciona un valor de tiempo (fecha) basado en el valor actual del reloj del sistema. También puede tener una resolución alta, pero tiene un inconveniente importante: este tipo de valor de tiempo puede verse afectado por diferentes ajustes de tiempo del sistema, es decir, cambio de zona horaria, cambio de horario de verano (DST), actualización del servidor NTP, hibernación del sistema y en. En algunas circunstancias, puede obtener un valor de tiempo transcurrido negativo que puede conducir a un comportamiento indefinido. En realidad, este tipo de fuente de tiempo es menos confiable que el primero.

Entonces la primera regla en la medición del intervalo de tiempo es usar un reloj monotónico si es posible. Por lo general, tiene una alta precisión y es confiable por diseño.

Estrategia de repliegue

Al implementar una solución portátil, vale la pena considerar una estrategia de respaldo: use un reloj monotónico si está disponible y recurra al enfoque de sellos de tiempo si no hay un reloj monotónico en el sistema.

Windows

Hay un excelente artículo llamado Adquirir sellos de tiempo de alta resolución en MSDN sobre la medición del tiempo en Windows que describe todos los detalles que puede necesitar saber sobre soporte de software y hardware. Para adquirir una marca de tiempo de alta precisión en Windows, debe:

  • consultar una frecuencia de temporizador (tics por segundo) con QueryPerformanceFrequency :

    LARGE_INTEGER tcounter; LARGE_INTEGER freq; if (QueryPerformanceFrequency (&tcounter) != 0) freq = tcounter.QuadPart;

    La frecuencia del temporizador se fija en el arranque del sistema, por lo que debe obtenerlo solo una vez.

  • consultar el valor actual de las QueryPerformanceCounter con QueryPerformanceCounter :

    LARGE_INTEGER tcounter; LARGE_INTEGER tick_value; if (QueryPerformanceCounter (&tcounter) != 0) tick_value = tcounter.QuadPart;

  • escala los tics al tiempo transcurrido, es decir, a microsegundos:

    LARGE_INTEGER usecs = (tick_value - prev_tick_value) / (freq / 1000000);

Según Microsoft, no debería tener ningún problema con este enfoque en Windows XP y versiones posteriores en la mayoría de los casos. Pero también puede usar dos soluciones de respaldo en Windows:

  • GetTickCount proporciona la cantidad de milisegundos que han transcurrido desde que se inició el sistema. Se envuelve cada 49,7 días, así que tenga cuidado al medir intervalos más largos.
  • GetTickCount64 es una versión de 64 bits de GetTickCount , pero está disponible desde Windows Vista y versiones posteriores.

OS X (macOS)

OS X (macOS) tiene sus propias unidades de tiempo absoluto Mach que representan un reloj monotónico. La mejor manera de comenzar es el artículo de Apple Technical Q & A QA1398: Mach Absolute Time Units que describe (con los ejemplos de código) cómo usar la API específica de Mach para obtener tics monótonos. También hay una pregunta local sobre esto llamada alternativa clock_gettime en Mac OS X que al final puede dejarte un poco confundido sobre qué hacer con el posible desbordamiento del valor porque la frecuencia del contador se usa en forma de numerador y denominador. Entonces, un pequeño ejemplo de cómo pasar el tiempo:

  • obtener el numerador y el denominador de la frecuencia del reloj:

    #include <mach/mach_time.h> #include <stdint.h> static uint64_t freq_num = 0; static uint64_t freq_denom = 0; void init_clock_frequency () { mach_timebase_info_data_t tb; if (mach_timebase_info (&tb) == KERN_SUCCESS && tb.denom != 0) { freq_num = (uint64_t) tb.numer; freq_denom = (uint64_t) tb.denom; } }

    Tienes que hacer eso solo una vez.

  • consulta el valor de marca actual con mach_absolute_time :

    uint64_t tick_value = mach_absolute_time ();

  • escala los tics al tiempo transcurrido, es decir, a microsegundos, usando el numerador y el denominador previamente consultados:

    uint64_t value_diff = tick_value - prev_tick_value; /* To prevent overflow */ value_diff /= 1000; value_diff *= freq_num; value_diff /= freq_denom;

    La idea principal para evitar un desbordamiento es reducir los tics a la precisión deseada antes de usar el numerador y el denominador. Como la resolución del temporizador inicial es en nanosegundos, lo dividimos por 1000 para obtener microsegundos. Puede encontrar el mismo enfoque utilizado en time_mac.c de time_mac.c . Si realmente necesita una precisión de nanosegundos, considere leer el ¿Cómo puedo usar mach_absolute_time sin desbordamiento? .

Linux y UNIX

La llamada clock_gettime es la mejor manera en cualquier sistema compatible con POSIX. Puede consultar el tiempo de diferentes fuentes de reloj, y el que necesitamos es CLOCK_MONOTONIC . No todos los sistemas que tienen clock_gettime admiten CLOCK_MONOTONIC , por lo que lo primero que debe hacer es verificar su disponibilidad:

  • si _POSIX_MONOTONIC_CLOCK está definido con un valor >= 0 significa que CLOCK_MONOTONIC está disponible;
  • si _POSIX_MONOTONIC_CLOCK está definido en 0 significa que también debe verificar si funciona en tiempo de ejecución, le sugiero que use sysconf :

    #include <unistd.h> #ifdef _SC_MONOTONIC_CLOCK if (sysconf (_SC_MONOTONIC_CLOCK) > 0) { /* A monotonic clock presents */ } #endif

  • de lo contrario, no se admite un reloj monótono y debe utilizar una estrategia de repliegue (consulte a continuación).

El uso de clock_gettime es bastante sencillo:

  • obtener el valor del tiempo:

    #include <time.h> #include <sys/time.h> #include <stdint.h> uint64_t get_posix_clock_time () { struct timespec ts; if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0) return (uint64_t) (ts.tv_sec * 1000000 + ts.tv_nsec / 1000); else return 0; }

    He reducido el tiempo a microsegundos aquí.

  • calcule la diferencia con el valor de tiempo anterior recibido de la misma manera:

    uint64_t prev_time_value, time_value; uint64_t time_diff; /* Initial time */ prev_time_value = get_posix_clock_time (); /* Do some work here */ /* Final time */ time_value = get_posix_clock_time (); /* Time difference */ time_diff = time_value - prev_time_value;

La mejor estrategia alternativa es usar la llamada gettimeofday : no es monótona, pero proporciona una resolución bastante buena. La idea es la misma que con clock_gettime , pero para obtener un valor de tiempo, debes:

#include <time.h> #include <sys/time.h> #include <stdint.h> uint64_t get_gtod_clock_time () { struct timeval tv; if (gettimeofday (&tv, NULL) == 0) return (uint64_t) (tv.tv_sec * 1000000 + tv.tv_usec); else return 0; }

De nuevo, el valor del tiempo se reduce a microsegundos.

SGI IRIX

IRIX tiene la llamada clock_gettime , pero carece de CLOCK_MONOTONIC . En cambio, tiene su propia fuente de reloj monotónica definida como CLOCK_SGI_CYCLE que debe usar en lugar de CLOCK_MONOTONIC con clock_gettime .

Solaris y HP-UX

Solaris tiene su propia interfaz de temporizador de alta resolución gethrtime que devuelve el valor del temporizador actual en nanosegundos. Aunque las versiones más recientes de Solaris pueden tener clock_gettime , puede atenerse a gethrtime si necesita admitir versiones antiguas de Solaris.

El uso es simple:

#include <sys/time.h> void time_measure_example () { hrtime_t prev_time_value, time_value; hrtime_t time_diff; /* Initial time */ prev_time_value = gethrtime (); /* Do some work here */ /* Final time */ time_value = gethrtime (); /* Time difference */ time_diff = time_value - prev_time_value; }

HP-UX no tiene clock_gettime , pero admite gethrtime que debe usar de la misma manera que en Solaris.

BeOS

BeOS también tiene su propia interfaz de temporizador de alta resolución system_time que devuelve la cantidad de microsegundos transcurridos desde que se inició la computadora.

Ejemplo de uso:

#include <kernel/OS.h> void time_measure_example () { bigtime_t prev_time_value, time_value; bigtime_t time_diff; /* Initial time */ prev_time_value = system_time (); /* Do some work here */ /* Final time */ time_value = system_time (); /* Time difference */ time_diff = time_value - prev_time_value; }

OS / 2

OS/2 tiene su propia API para recuperar marcas de tiempo de alta precisión:

  • consultar una frecuencia de temporizador (tics por unidad) con DosTmrQueryFreq (para el compilador de GCC):

    #define INCL_DOSPROFILE #define INCL_DOSERRORS #include <os2.h> #include <stdint.h> ULONG freq; DosTmrQueryFreq (&freq);

  • consulta el valor actual de las DosTmrQueryTime con DosTmrQueryTime :

    QWORD tcounter; unit64_t time_low; unit64_t time_high; unit64_t timestamp; if (DosTmrQueryTime (&tcounter) == NO_ERROR) { time_low = (unit64_t) tcounter.ulLo; time_high = (unit64_t) tcounter.ulHi; timestamp = (time_high << 32) | time_low; }

  • escala los tics al tiempo transcurrido, es decir, a microsegundos:

    uint64_t usecs = (prev_timestamp - timestamp) / (freq / 1000000);

Ejemplo de implementación

Puede echar un vistazo a la biblioteca de plibsys que implementa todas las estrategias descritas anteriormente (consulte ptimeprofiler * .c para obtener más información).


No existe una función ANSI C que proporcione una resolución de tiempo superior a 1 segundo, pero la función POSIX gettimeofday brinda una resolución de microsegundos. La función de reloj solo mide la cantidad de tiempo que un proceso ha estado ejecutando y no es precisa en muchos sistemas.

Puede usar esta función de esta manera:

struct timeval tval_before, tval_after, tval_result; gettimeofday(&tval_before, NULL); // Some code you want to time, for example: sleep(1); gettimeofday(&tval_after, NULL); timersub(&tval_after, &tval_before, &tval_result); printf("Time elapsed: %ld.%06ld/n", (long int)tval_result.tv_sec, (long int)tval_result.tv_usec);

Esto devuelve Time elapsed: 1.000870 en mi máquina.


#include <time.h> clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);