¿Se garantiza gettimeofday () una resolución de microsegundos?

votos
82

Transmitimos un juego, originalmente escrito para la API Win32, a Linux (bueno, portando el puerto OS X del puerto Win32 a Linux).

Implementé QueryPerformanceCounterdando uSeconds desde el inicio del proceso:

BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
    gettimeofday(&currentTimeVal, NULL);
    performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec);
    performanceCount->QuadPart *= (1000 * 1000);
    performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec);

    return true;
}

Esto, junto con QueryPerformanceFrequency()dar una frecuencia constante de 1000000, funciona bien en mi máquina , dándome una variable de 64 bits que contiene uSecondsdesde el inicio del programa.

Entonces, ¿ es esto portátil? No quiero descubrir que funciona de manera diferente si el kernel fue compilado de una determinada manera o algo por el estilo. Sin embargo, estoy de acuerdo con que no sea portátil para algo que no sea Linux.

Publicado el 01/08/2008 a las 15:36
fuente por usuario
En otros idiomas...                            


10 respuestas

votos
54

Tal vez. Pero tienes problemas más grandes. gettimeofday()puede dar como resultado tiempos incorrectos si hay procesos en su sistema que cambian el temporizador (es decir, ntpd). En un Linux "normal", creo que la resolución de gettimeofday()es 10us. Puede saltar hacia adelante y hacia atrás y el tiempo, en consecuencia, en función de los procesos que se ejecutan en su sistema. Esto efectivamente hace que la respuesta a su pregunta no.

Deberías buscar clock_gettime(CLOCK_MONOTONIC)los intervalos de tiempo. Sufre de varios problemas menores debido a cosas como sistemas multi-core y configuraciones de reloj externo.

Además, mira en la clock_getres()función.

Respondida el 01/08/2008 a las 15:53
fuente por usuario

votos
39

Alta resolución, baja sobrecarga para procesadores Intel

Si tiene hardware Intel, a continuación le mostramos cómo leer el contador de instrucciones en tiempo real de la CPU. Le indicará la cantidad de ciclos de CPU ejecutados desde que se inició el procesador. Este es probablemente el contador de grano más fino que puede obtener para medir el rendimiento.

Tenga en cuenta que este es el número de ciclos de CPU. En Linux, puede obtener la velocidad de CPU de / proc / cpuinfo y dividir para obtener el número de segundos. Convertir esto a un doble es bastante útil.

Cuando ejecuto esto en mi caja, obtengo

11867927879484732
11867927879692217
it took this long to call printf: 207485

Aquí está la guía para desarrolladores de Intel que brinda muchísimos detalles.

#include <stdio.h>
#include <stdint.h>

inline uint64_t rdtsc() {
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx");
    return (uint64_t)hi << 32 | lo;
}

main()
{
    unsigned long long x;
    unsigned long long y;
    x = rdtsc();
    printf("%lld\n",x);
    y = rdtsc();
    printf("%lld\n",y);
    printf("it took this long to call printf: %lld\n",y-x);
}
Respondida el 02/08/2008 a las 09:08
fuente por usuario

votos
18

@Bernardo:

Debo admitir que la mayoría de tu ejemplo fue directo a mi cabeza. Sin embargo, compila y parece funcionar. ¿Es esto seguro para sistemas SMP o SpeedStep?

Esa es una buena pregunta ... Creo que el código está bien. Desde un punto de vista práctico, lo usamos en mi compañía todos los días, y corremos en una amplia gama de cajas, todo desde 2 hasta 8 núcleos. Por supuesto, YMMV, etc., pero parece ser un método confiable y de bajo costo (porque no hace que el contexto cambie al espacio del sistema).

En general, cómo funciona es:

  • declara que el bloque de código es ensamblador (y volátil, por lo que el optimizador lo dejará en paz).
  • ejecutar la instrucción CPUID. Además de obtener cierta información de la CPU (con la que no hacemos nada) sincroniza el buffer de ejecución de la CPU para que las temporizaciones no se vean afectadas por la ejecución fuera de orden.
  • ejecuta la ejecución de rdtsc (lectura de marca de tiempo). Esto obtiene la cantidad de ciclos de la máquina ejecutados desde que se reinició el procesador. Este es un valor de 64 bits, por lo que con las velocidades de CPU actuales se ajustará cada 194 años más o menos. Curiosamente, en la referencia original de Pentium, notan que se envuelve cada 5800 años más o menos.
  • las últimas dos líneas almacenan los valores de los registros en las variables hi y lo, y lo ponen en el valor de retorno de 64 bits.

Notas específicas:

  • la ejecución fuera de orden puede causar resultados incorrectos, por lo que ejecutamos la instrucción "cpuid" que, además de proporcionarle cierta información sobre la CPU, también sincroniza cualquier ejecución de instrucción fuera de orden.

  • La mayoría de los sistemas operativos sincronizan los contadores en las CPU cuando comienzan, por lo que la respuesta es buena en un par de nano segundos.

  • El comentario de hibernación probablemente sea cierto, pero en la práctica probablemente no te preocupen los tiempos a través de los límites de hibernación.

  • con respecto al speedstep: CPUs Intel más recientes compensan los cambios de velocidad y devuelve un recuento ajustado. Hice un escaneo rápido de algunas de las cajas en nuestra red y encontré solo una caja que no la tenía: un Pentium 3 que ejecutaba un viejo servidor de base de datos. (Estos son cuadros de Linux, así que lo comprobé con: grep constant_tsc / proc / cpuinfo)

  • No estoy seguro acerca de las CPUs de AMD, somos principalmente una tienda de Intel, aunque sé que algunos de nuestros gurús de sistemas de bajo nivel hicieron una evaluación de AMD.

Espero que esto satisfaga tu curiosidad, es un área de programación interesante e inmemorial (en mi humilde opinión). ¿Sabes cuándo Jeff y Joel estaban hablando sobre si un programador debería saber o no C? Les estaba gritando: "Olviden las cosas C de alto nivel ... ¡el ensamblador es lo que debe aprender si quiere saber qué está haciendo la computadora!"

Respondida el 04/08/2008 a las 01:51
fuente por usuario

Respondida el 18/08/2008 a las 16:51
fuente por usuario

votos
11

Wine está usando gettimeofday () para implementar QueryPerformanceCounter () y se sabe que muchos juegos de Windows funcionan en Linux y Mac.

Comienza http://source.winehq.org/source/dlls/kernel32/cpu.c#L312

conduce a http://source.winehq.org/source/dlls/ntdll/time.c#L448

Respondida el 04/08/2008 a las 15:44
fuente por usuario

votos
9

Entonces dice microsegundos explícitamente, pero dice que la resolución del reloj del sistema no está especificada. Supongo que la resolución en este contexto significa cómo se incrementará la cantidad más pequeña alguna vez?

La estructura de datos se define como tener microsegundos como unidad de medida, pero eso no significa que el reloj o el sistema operativo sean realmente capaces de medirlo con precisión.

Al igual que otras personas han sugerido, gettimeofday()es malo porque establecer el tiempo puede provocar un sesgo en el reloj y deshacerse de su cálculo. clock_gettime(CLOCK_MONOTONIC)es lo que quieres, y clock_getres()te dirá la precisión de tu reloj.

Respondida el 02/08/2008 a las 18:57
fuente por usuario

votos
8

La resolución real de gettimeofday () depende de la arquitectura del hardware. Los procesadores Intel y las máquinas SPARC ofrecen temporizadores de alta resolución que miden microsegundos. Otras arquitecturas de hardware recurren al temporizador del sistema, que generalmente se establece en 100 Hz. En tales casos, la resolución de tiempo será menos precisa.

Obtuve esta respuesta de Medición de tiempo y temporizadores de alta resolución, Parte I

Respondida el 01/08/2008 a las 15:55
fuente por usuario

votos
5

Esta respuesta menciona problemas con el reloj siendo ajustados. Tanto los problemas que garantizan unidades de garrapatas y los problemas con el tiempo siendo ajustados son resueltos en C ++ 11 con la <chrono>biblioteca.

El reloj std::chrono::steady_clockno se garantiza que se puede modificar, y además se avanzará a un ritmo constante con respecto al tiempo real, así como tecnologías SpeedStep no debe afectar a la misma.

Puede obtener unidades typesafe mediante la conversión a una de las std::chrono::durationespecializaciones, tales como std::chrono::microseconds. Con este tipo no hay ambigüedad sobre las unidades utilizadas por el valor de la garrapata. Sin embargo, tener en cuenta que el reloj no tiene necesariamente que esta resolución. Puede convertir una duración de attosegundos sin necesidad de tener un reloj que precisa.

Respondida el 26/06/2012 a las 16:57
fuente por usuario

votos
4

Según mi experiencia, y según lo que he leído en Internet, la respuesta es "No", no está garantizado. Depende de la velocidad de la CPU, el sistema operativo, el sabor de Linux, etc.

Respondida el 01/08/2008 a las 15:46
fuente por usuario

votos
3

Leer el RDTSC no es confiable en sistemas SMP, ya que cada CPU mantiene su propio contador y no se garantiza que cada contador esté sincronizado con respecto a otra CPU.

Podría sugerir intentarlo clock_gettime(CLOCK_REALTIME). El manual de posix indica que esto debe implementarse en todos los sistemas compatibles. Puede proporcionar un recuento de nanosegundos, pero es probable que desee comprobar clock_getres(CLOCK_REALTIME)en su sistema para ver cuál es la resolución real.

Respondida el 18/08/2008 a las 16:40
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more