2008-08-01 15 views
80

Así que me encuentro portando un juego, que fue escrito originalmente para la API de Win32, a Linux (bueno, portando el puerto OS X del puerto Win32 a Linux). He implementado QueryPerformanceCounter dando a los uSeconds ya que el proceso de puesta en marcha:¿Se garantiza gettimeofday() una resolución de microsegundos?

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 constante 1000000 como la frecuencia, funciona bien en mi máquina, dándome una variable de 64 bits que contiene uSeconds desde el inicio del programa. ¿Entonces es portátil? No quiero descubrir que funciona de manera diferente si el kernel se compiló de cierta manera o algo así. Sin embargo, estoy de acuerdo con que no sea portátil para algo que no sea Linux.

Respuesta

53

Quizás. 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.

Debe consultar clock_gettime(CLOCK_MONOTONIC) para conocer los intervalos de tiempo. Sufre de varios problemas menores debido a cosas como sistemas multi-core y configuraciones de reloj externo.

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

+1

clock_gettime está presente solo en el último Linux. otro sistema solo tiene gettimeofday() –

+3

@ vitaly.v.ch es POSIX así que no es solo Linux y 'newist'? incluso las distribuciones 'Enterprise' como Red Hat Enterprise Linux se basan en 2.6.18 que tiene clock_gettime por lo que no, no son muy nuevas.(La fecha de la página del manual en RHEL es 2004-marzo-12, por lo que ha estado disponible por un tiempo) a menos que esté hablando de ANTIGUO FREAKING de kernels antiguos ¿A qué se refiere WTF? – Spudd86

+0

clock_gettime se incluyó en POSIX en 2001. Hasta ahora conozco clock_gettime() implementado en Linux 2.6 y qnx. pero Linux 2.4 se usa actualmente en muchos sistemas de producción. –

4

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

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 High Resolution Time Measurement and Timers, Part I

39

alta resolución, Timing bajo costo operativo para procesadores Intel

Si estás en hardware de Intel, aquí está 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 la 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, me sale

11867927879484732 
11867927879692217 
it took this long to call printf: 207485 

Aquí está la Intel developer's guide que da un montón de 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); 
} 
+11

Tenga en cuenta que el TSC no siempre se puede sincronizar entre los núcleos, puede detener o cambiar su frecuencia cuando el procesador ingresa a modos de potencia inferiores (y usted no tiene forma de saberlo), y en general no siempre es confiable. El kernel puede detectar cuándo es confiable, detectar otras alternativas como HPET y el temporizador ACPI PM, y seleccionar automáticamente la mejor. Es una buena idea usar siempre el núcleo para el tiempo, a menos que esté realmente seguro de que el TSC es estable y monótono. – CesarB

+11

El TSC en Core y las plataformas superiores de Intel se sincroniza en múltiples CPUs * y * incrementos a una frecuencia constante independiente de los estados de administración de energía. Consulte el Manual del desarrollador de software Intel, vol. 3 Sección 18.10. Sin embargo, la velocidad a la que el contador aumenta es * no * la misma que la frecuencia de la CPU. El TSC se incrementa a "la frecuencia resuelta máxima de la plataforma, que es igual al producto de la frecuencia de bus escalable y la relación de bus resuelto máximo". Manual del desarrollador de software de Intel, vol. 3 Sección 18.18.5. Obtiene esos valores de los registros específicos de modelo (MSR) de la CPU. – sstock

+7

Puede obtener la frecuencia de bus escalable y la relación de bus resuelto máximo consultando los registros específicos de modelo de la CPU (MSR) de la siguiente manera: Frecuencia de bus escalable == MSR_FSB_FREQ [2: 0] id 0xCD, Relación de bus resuelto máximo == MSR_PLATFORM_ID [12 : 8] id 0x17. Consulte el Apéndice B.1 de Intel SDM Vol.3 para interpretar los valores de registro. Puede usar las herramientas msr en Linux para consultar los registros. http://www.kernel.org/pub/linux/utils/cpu/msr-tools/ – sstock

9

Por lo que dice explícitamente microsegundos, pero dice que la resolución del reloj del sistema es indeterminado.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 quiere, y clock_getres() le dirá la precisión de su reloj.

+0

Entonces, ¿qué sucede en su código cuando gettimeofday() salta hacia delante o hacia atrás con el horario de verano? – mpez0

+3

clock_gettime está presente solo en el último Linux. otro sistema solo tiene gettimeofday() –

+0

@ mpez0 no es – Spudd86

18

@Bernard:

Tengo que admitir, la mayor parte de su ejemplo fueron directamente sobre 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 empresa 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 de bajo costo (porque no hace un cambio de contexto en el espacio del sistema) método de sincronización.

general cómo funciona es:

  • declaran el bloque de código ensamblador para ser (y volátil, por lo que el optimizador dejarlo solo).
  • ejecutar la instrucción CPUID. Además de obtener información sobre la CPU (con la que no hacemos nada) sincroniza la memoria intermedia de ejecución de la CPU para que las temporizaciones no se vean afectadas por la ejecución fuera de servicio.
  • ejecutar la ejecución de rdtsc (lectura de marca de tiempo). Esto recupera el número de ciclos de máquina ejecutados desde que se reinició el procesador. Este es un valor de 64 bits, por lo que con las velocidades actuales de la CPU se ajustará cada 194 años más o menos. Curiosamente, en la referencia original de Pentium, observan que se ajusta a 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:

  • fuera de orden de ejecución puede causar resultados incorrectos, por lo que ejecutar la instrucción "cpuid" que además de darle un poco de información acerca de la CPU también se sincroniza cualquier ejecución de instrucción fuera de orden.

  • La mayoría de los sistemas operativos sincronizan los contadores en las CPU cuando se inician, 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 se preocupe por los tiempos en los límites de hibernación.

  • para speedstep: CPUs Intel más recientes compensan la velocidad cambia y devuelve un recuento ajustado. Hice un escaneo rápido sobre algunos de los cuadros en nuestra red y encontré que solo una casilla que no tenía: un Pentium 3 que ejecutaba un viejo servidor de base de datos. (estos son cajas de Linux, por lo que he comprobado con: grep constant_tsc/proc/cpuinfo)

  • No estoy seguro acerca de la CPU de AMD, estamos ante todo una tienda de Intel, aunque sé que algunos de nuestros baja Los gurús de sistemas de nivel hicieron una evaluación AMD .

Hope esto satisface su curiosidad, que es un área interesante y (en mi humilde opinión) atención en los estudios de la programación. ¿Sabes cuándo Jeff y Joel estaban hablando sobre si un programador debería saber o no C? Estaba gritándoles, "¡olvidémonos de las cosas C de alto nivel ... el ensamblador es lo que debería aprender si quiere saber qué está haciendo la computadora !"

+1

... La gente del kernel ha estado intentando que la gente deje de usar rdtsc por un tiempo ... y generalmente evita usarlo en el kernel porque es poco confiable . – Spudd86

+1

Como referencia, la pregunta que hice (en una respuesta separada, antes de los comentarios) fue: "Tengo que admitir que la mayoría de tu ejemplo pasó por encima de mi cabeza. Sin embargo, compila y parece funcionar. ¿Es seguro? para sistemas SMP o SpeedStep? " – Bernard

3

Lectura de la RDTSC no es fiable en sistemas SMP, ya que cada CPU mantiene su propio contador y cada contador no se garantiza que por sincronizada 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.

+0

'clock_getres (CLOCK_REALTIME)' no dará la resolución real. Siempre devuelve "1 ns" (un nanosegundo) cuando los hrtimers están disponibles, verifique el archivo 'include/linux/hrtimer.h' para' definir HIGH_RES_NSEC 1' (más en http://stackoverflow.com/a/23044075/196561) – osgx

5

This answer menciones problemas con el reloj está ajustando. Tanto sus problemas que garantizan unidades de ticks como los problemas con el tiempo ajustado se resuelven en C++ 11 con la biblioteca <chrono>.

Se garantiza que el reloj std::chrono::steady_clock no se ajustará, y además avanzará a una tasa 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.

Cuestiones relacionadas