Estoy portando un juego, que fue escrito originalmente para la API Win32, a Linux (bueno, portando el puerto OS X del puerto Win32 a Linux).
Lo he implementado QueryPerformanceCounter
dando los uSeconds desde que se inició el proceso:
BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
gettimeofday(¤tTimeVal, 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 constante de 1000000 como frecuencia, funciona bien en mi máquina , dándome una variable de 64 bits que contiene uSeconds
desde el inicio del programa.
Entonces, ¿ es esto 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 a algo que no sea Linux.
Temporización de alta resolución y baja sobrecarga para procesadores Intel
Si tiene hardware Intel, aquí le mostramos cómo leer el contador de instrucciones en tiempo real de la CPU. Le dirá el número de ciclos de CPU ejecutados desde que se inició el procesador. Este es probablemente el contador más detallado 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 la CPU de / proc / cpuinfo y dividir para obtener el número de segundos. Convertir esto en un doble es bastante útil.
Cuando ejecuto esto en mi caja, obtengo
Aquí está la guía para desarrolladores de Intel que brinda muchos detalles.
fuente
CPUID
volver a usarse después de la primeraRDTSC
instrucción y antes de ejecutar el código que se está comparando? De lo contrario, ¿qué impedirá que el código comparativo se ejecute antes o en paralelo con el primeroRDTSC
y, en consecuencia, esté subrepresentado en elRDTSC
delta?@Bernardo:
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 ejecutamos una amplia gama de cajas, de 2 a 8 núcleos. Por supuesto, YMMV, etc., pero parece ser un método de sincronización confiable y de bajo costo (porque no hace un cambio de contexto al espacio del sistema).
Generalmente, cómo funciona es:
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 darle alguna 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 se inician, por lo que la respuesta es buena en un par de nanosegundos.
El comentario de hibernación probablemente sea cierto, pero en la práctica probablemente no le importen los tiempos a través de los límites de hibernación.
con respecto al paso de velocidad: las CPU Intel más nuevas compensan los cambios de velocidad y devuelven un recuento ajustado. Hice un escaneo rápido en algunas de las cajas en nuestra red y encontré solo una caja que no lo tenía: un Pentium 3 que ejecutaba un servidor de base de datos antiguo. (estas son cajas de Linux, así que verifiqué con: grep constant_tsc / proc / cpuinfo)
No estoy seguro acerca de las CPU 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 su curiosidad, es un área de programación interesante y (en mi humilde opinión) poco estudiada. ¿Sabes cuando Jeff y Joel hablaban sobre si un programador debería conocer C? Les gritaba: "Olvídense de ese material C de alto nivel ... ¡ensamblador es lo que debe aprender si quiere saber qué está haciendo la computadora!"
fuente
Puede estar interesado en las preguntas frecuentes de Linux para
clock_gettime(CLOCK_REALTIME)
fuente
Wine está usando gettimeofday () para implementar QueryPerformanceCounter () y se sabe que hace que muchos juegos de Windows funcionen en Linux y Mac.
Inicia http://source.winehq.org/source/dlls/kernel32/cpu.c#L312
conduce a http://source.winehq.org/source/dlls/ntdll/time.c#L448
fuente
La estructura de datos se define como microsegundos como unidad de medida, pero eso no significa que el reloj o el sistema operativo sea realmente capaz de medir eso con precisión.
Como han sugerido otras personas,
gettimeofday()
es malo porque fijar la hora puede hacer que el reloj se desvíe y desvíe el cálculo.clock_gettime(CLOCK_MONOTONIC)
es lo que quieres yclock_getres()
te dirá la precisión de tu reloj.fuente
Obtuve esta respuesta de Medición de tiempo y temporizadores de alta resolución, Parte I
fuente
Esta respuesta menciona problemas con el ajuste del reloj. Tanto sus problemas de garantía de unidades de ticks como los problemas de ajuste de tiempo se resuelven en C ++ 11 con la
<chrono>
biblioteca.Se
std::chrono::steady_clock
garantiza que el reloj 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 con seguridad de tipos convirtiendo a una de las
std::chrono::duration
especializaciones, comostd::chrono::microseconds
. Con este tipo no hay ambigüedad sobre las unidades utilizadas por el valor de tick. Sin embargo, tenga en cuenta que el reloj no necesariamente tiene esta resolución. Puede convertir una duración a attosegundos sin tener un reloj tan preciso.fuente
Por 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, el sistema operativo, el sabor de Linux, etc.
fuente
La lectura del RDTSC no es confiable en los 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 que lo intentes
clock_gettime(CLOCK_REALTIME)
. El manual posix indica que esto debe implementarse en todos los sistemas compatibles. Puede proporcionar un recuento de nanosegundos, pero probablemente querrá verificarclock_getres(CLOCK_REALTIME)
su sistema para ver cuál es la resolución real.fuente
clock_getres(CLOCK_REALTIME)
no dará la resolución real. Siempre devuelve "1 ns" (un nanosegundo) cuando los hrtimers están disponibles, verifique elinclude/linux/hrtimer.h
archivodefine HIGH_RES_NSEC 1
(más en stackoverflow.com/a/23044075/196561 )