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

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

Implementé QueryPerformanceCounter dando 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() dando una frecuencia constante de 1000000, funciona bien en mi máquina , dándome una variable de 64 bits que contiene uSeconds desde el inicio del progtwig.

Entonces, ¿ es esto portátil? No quiero descubrir que funciona de manera diferente si el kernel fue comstackdo 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.

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ía mirar en clock_gettime(CLOCK_MONOTONIC) para 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 función clock_getres() .

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

@Bernardo:

Debo admitir que la mayoría de tu ejemplo fue directo a mi cabeza. Sin embargo, comstack 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 progtwigción interesante e inmemorial (en mi humilde opinión). ¿Sabes cuándo Jeff y Joel estaban hablando sobre si un progtwigdor 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!”

Puede que esté interesado en las preguntas frecuentes de Linux para clock_gettime(CLOCK_REALTIME)

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

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 la hora puede provocar un sesgo en el reloj y deshacerse de su cálculo. clock_gettime(CLOCK_MONOTONIC) es lo que quiere, y clock_getres() le dirá la precisión de su reloj.

La resolución real de gettimeofday () depende de la architecture del hardware. Los procesadores Intel y las máquinas SPARC ofrecen temporizadores de alta resolución que miden microsegundos. Otras architectures 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

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.

Esta respuesta menciona problemas con el ajuste del reloj. Tanto sus problemas que garantizan unidades de ticks como los problemas con el tiempo ajustado se resuelven en C ++ 11 con la biblioteca .

Se garantiza que el reloj std::chrono::steady_clock no se ajustará y, además, avanzará a una velocidad constante en relación con el tiempo real, por lo que tecnologías como SpeedStep no deben afectarlo.

Puede obtener unidades de tipo seguro mediante la conversión a una de las especializaciones std::chrono::duration , como std::chrono::microseconds . Con este tipo no hay ambigüedad sobre las unidades utilizadas por el valor de tilde. Sin embargo, tenga en cuenta que el reloj no necesariamente tiene esta resolución. Puede convertir una duración en attosegundos sin tener realmente un reloj tan preciso.

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