Pregunta

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

¿Fue útil?

Solución

No hay función ANSI C que proporciona mejor que 1 segundo resolución de tiempo pero la función POSIX gettimeofday proporciona una resolución de microsegundos. La función de reloj sólo mide la cantidad de tiempo que un proceso ha pasado la ejecución y no es precisa en muchos sistemas.

Puede utilizar esta función como esta:

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.

Otros consejos

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

Yo siempre uso la función clock_gettime (), volviendo hora del reloj CLOCK_MONOTONIC. El tiempo devuelto es la cantidad de tiempo, en segundos y nanosegundos, desde un cierto punto no especificado en el pasado, tales como 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);
}

La implementación de una solución portátil

Como ya se mencionó aquí que no hay una solución adecuada ANSI con la suficiente precisión para el problema de la medición del tiempo, quiero escribir sobre las maneras cómo conseguir un portátil y, si es posible, una solución de medición de tiempo de alta resolución.

reloj Monotónica vs marcas de tiempo

En términos generales hay dos maneras de medición del tiempo:

  • reloj monotónica;
  • actual (fecha) marca de tiempo.

El primero utiliza un contador de reloj monótona (a veces se le llama un contador de paso) que cuenta garrapatas con una frecuencia predefinida, por lo que si usted tiene un valor garrapatas y se conoce la frecuencia, se puede convertir fácilmente las garrapatas al tiempo transcurrido. En realidad, no se garantiza que un reloj monótona refleja la hora actual del sistema de ninguna manera, sino que también puede contar garrapatas desde un inicio del sistema. Pero garantiza que un reloj siempre se ejecuta en un modo creciente, independientemente del estado del sistema. Por lo general, la frecuencia está ligada a una fuente de alta resolución de hardware, es por eso que proporciona una alta precisión (depende del hardware, pero la mayor parte del hardware moderno no tiene problemas con las fuentes de reloj de alta resolución).

La segunda manera proporciona un valor de tiempo (fecha) basado en el valor actual del reloj del sistema. También puede tener una alta resolución, pero tiene un gran inconveniente: este tipo de valor de tiempo puede verse afectada por diferentes ajustes de tiempo del sistema, es decir, el cambio de zona horaria, el horario de verano (DST) de cambio, de actualización del servidor NTP, la hibernación del sistema y por lo en. En algunas circunstancias se 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 fiable que el primero.

Así que la primera regla de medición de intervalo de tiempo es el uso de un reloj monótona si es posible. Por lo general, tiene una alta precisión, y es fiable por diseño.

estrategia de retorno

Cuando se implementa una solución portátil que vale la pena tener en cuenta una estrategia de repliegue:. Usar un reloj monótona si está disponible y vuelve a los sellos enfoque de tiempo si no hay reloj monotónico en el sistema

Windows

Hay un gran artículo llamado la adquisición de las marcas de tiempo de alta resolución en MSDN sobre la medición del tiempo en Windows que describe todos los detalles que pueda necesitar saber sobre software y soporte de hardware. Para adquirir un sello de tiempo de alta precisión en Windows debe:

  • consultar una frecuencia temporizador (garrapatas 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 necesita para obtener una sola vez.

  • consultar la corriente garrapatas valor con QueryPerformanceCounter :?

    LARGE_INTEGER tcounter;
    LARGE_INTEGER tick_value;
    
    if (QueryPerformanceCounter (&tcounter) != 0)
        tick_value = tcounter.QuadPart;
    
  • escalar las garrapatas a tiempo transcurrido, es decir, a microsegundos:

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

De acuerdo con Microsoft que 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 se puede utilizar dos soluciones de repliegue en Windows:

  • GetTickCount proporciona el número de milisegundos que han transcurrido desde que el sistema de empezó. Se envuelve cada 49,7 días, así que tenga cuidado en la medición de intervalos más largos.
  • GetTickCount64 es una versión de 64 bits de GetTickCount, pero está disponible a partir de Windows Vista o superior.

OS X (macOS)

OS X (macOS) tiene sus propias unidades de tiempo absolutos Mach que Represent un reloj monótona. La mejor manera de empezar es Técnico Q y A QA1398 de Apple artículo : Unidades Mach tiempo absoluto que describe (con los ejemplos de código) cómo utilizar la API de Mach-específica para obtener las garrapatas monótonas. Hay también una cuestión local sobre lo llama en Mac OS X lo que al final le puede dejar un poco confundido qué hacer con la posible desbordamiento de valor debido a que la frecuencia del contador se utiliza en forma de numerador y denominador. Por lo tanto, un breve ejemplo cómo conseguir tiempo transcurrido:

  • obtener el numerador y el denominador frecuencia de 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;
        }
    }
    

    Es necesario hacer que sólo una vez.

  • consultar el valor garrapata actual con mach_absolute_time:

    uint64_t tick_value = mach_absolute_time ();
    
  • escalar las garrapatas a tiempo transcurrido, es decir, a microsegundos, utilizando numerador y el denominador previamente consultada:

    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 de bajar las garrapatas a la precisión deseada antes de usar el numerador y el denominador. A medida que la resolución del temporizador inicial es en nanosegundos, lo dividimos por 1000 para obtener microsegundos. Usted puede encontrar el mismo método utilizado en time_mac.c . Si realmente necesita una precisión de nanosegundos considerar leer el ¿Cómo puedo usar mach_absolute_time sin desbordar? .

Linux y UNIX

La llamada clock_gettime es la mejor manera en cualquier sistema POSIX ambiente. Se puede consultar el tiempo a partir de diferentes fuentes de reloj, y la que necesitamos es CLOCK_MONOTONIC. No todos los sistemas que tienen _POSIX_MONOTONIC_CLOCK apoyo >= 0, así que lo primero que tiene que hacer es comprobar su disponibilidad:

  • si 0 se define a un valor sysconf significa que gettimeofday es disponible;
  • Si CLOCK_SGI_CYCLE se define a gethrtime que significa que usted debe comprobar, además, si funciona en tiempo de ejecución, sugiero utilizar system_time:

    #include <unistd.h>
    
    #ifdef _SC_MONOTONIC_CLOCK
    if (sysconf (_SC_MONOTONIC_CLOCK) > 0) {
        /* A monotonic clock presents */
    }
    #endif
    
  • otra manera monótona un reloj no es compatible y se debe utilizar un estrategia de retorno (ver más abajo).

El uso de DosTmrQueryFreq es bastante sencillo:

  • obtener el valor de 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 tiempo de microsegundos aquí.

  • calcular la diferencia con el valor anterior vez recibida 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 de repliegue es el uso de la llamada DosTmrQueryTime: no es una monótona, pero ofrece muy buena resolución. La idea es la misma que con <=>, pero para obtener un valor de tiempo debe:

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

Una vez más, el valor de tiempo se escala hacia abajo para microsegundos.

SGI IRIX

IRIX tiene la llamada <=>, pero carece de <=>. En cambio, ha definido su propia fuente de reloj monótona como <=> que se debe utilizar en lugar de <=> con <=>.

Solaris y HP-UX

Solaris tiene su propia interfaz temporizador de alta resolución <=> que devuelve el valor actual del temporizador en nanosegundos. A pesar de las nuevas versiones de Solaris pueden tener <=>, que puede pegarse a <=> si es necesario para apoyar las versiones anteriores 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 carece <=>, pero soporta <=> que se debe utilizar en la misma forma que en Solaris.

BeOS

BeOS también tiene su propia interfaz de temporizador de alta resolución <=> que devuelve el número de microsegundos que han transcurrido desde que el equipo se inicia.

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 las marcas de tiempo de alta precisión:

  • query una frecuencia de temporizador (garrapatas por unidad) con <=> (para compilador GCC):

    #define INCL_DOSPROFILE
    #define INCL_DOSERRORS
    #include <os2.h>
    #include <stdint.h>
    
    ULONG freq;
    
    DosTmrQueryFreq (&freq);
    
  • query la corriente garrapatas valor con <=>:

    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;
    }
    
  • escalar las garrapatas a tiempo transcurrido, es decir, a microsegundos:

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

Ejemplo aplicación

Puede echar un vistazo a la plibsys biblioteca que implementa todas las estrategias descrito anteriormente (véase ptimeprofiler * .c para más detalles).

timespec_get desde C11

Devuelve hasta nanosegundos, redondeado a la resolución de la aplicación.

Parece que un timo ANSI de POSIX'clock_gettime.

Ejemplo: un 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 C11 N1570 proyecto de norma de 7.27.2.5 "La función timespec_get dice":

  

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

     

321) Aunque un objeto struct timespec describe veces con resolución de nanosegundos, la disposición   resolución depende del sistema y puede ser incluso mayor que 1 segundo.

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

glibc 2.21 aplicación

Puede ser encontrado 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:

  • Sólo TIME_UTC está soportado actualmente

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

    x86-64 Linux tiene una llamada man clock_gettime sistema.

    Tenga en cuenta que esto no es una prueba de fallos micro-evaluación comparativa método porque:

    • getrusage() dice que esta medida puede tener discontinuidades si cambia algún ajuste de la hora del sistema, mientras que su programa se ejecuta. Esto debería ser un evento raro, por supuesto, y usted podría ser capaz de ignorarlo.

    • Este mide el tiempo de la pared, por lo que si el programador decide olvidarse de su tarea, aparecerá para funcionar durante más tiempo.

    Por estas razones <=> podría ser una mejor mejor herramienta de evaluación comparativa POSIX, a pesar de su menor precisión máxima microsegundo.

    Más información en: Medida tiempo en Linux - el tiempo del reloj vs vs vs getrusage clock_gettime vs vs gettimeofday timespec_get

La mejor precisión que se puede conseguir posiblemente es a través del uso de la única x86 instrucción "RDTSC", que puede proporcionar una resolución a nivel de reloj (ne debe, por supuesto tener en cuenta el coste de la RDTSC llamarse a sí mismo, que puede ser medir fácilmente al iniciar la aplicación).

El problema principal aquí es la medición de la cantidad de ciclos por segundo, lo que no debe ser demasiado duro.

La respuesta aceptada es bueno enough.But mi solución es más simple.I acaba de prueba en Linux, el uso gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0.

Alse uso gettimeofday, la tv_sec es la parte del segundo, y los 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;
 }

impresión:

1522139691342 1522139692342, exactamente un segundo.

En Windows:

SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top