Pregunta

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

he implementado QueryPerformanceCounter dando los uSegundos desde que se inició el 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, aunado a QueryPerformanceFrequency() dando una constante 1000000 como frecuencia, funciona bien en mi maquina, 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.

¿Fue útil?

Solución

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

Deberías investigar clock_gettime(CLOCK_MONOTONIC) para intervalos de tiempo.Sufre varios problemas menores debido a cosas como sistemas multinúcleo y configuraciones de reloj externo.

Además, mire el clock_getres() función.

Otros consejos

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 indicará la cantidad 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 dividirla para obtener la cantidad de segundos.Convertir esto a doble es bastante útil.

Cuando ejecuto esto en mi caja, obtengo

11867927879484732
11867927879692217
it took this long to call printf: 207485

Aquí esta la guía del desarrollador Intel eso da toneladas 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);
}

@Bernardo:

Debo admitir que la mayor parte de tu ejemplo me pasó por alto.Sin embargo, se compila y parece funcionar.¿Es esto seguro para sistemas SMP o SpeedStep?

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 gran variedad de cajas, de todo, desde 2-8 núcleos.Por supuesto, YMMV, etc., pero parece ser un método de sincronización confiable y bajo (porque no hace un cambio de contexto en el espacio del sistema).

Generalmente como funciona es:

  • Declare que el bloque de código es ensamblador (y volátil, para que el optimizador lo dejará solo).
  • ejecutar la instrucción CPUID.Además de obtener información de CPU (con la que no hacemos nada), sincroniza el búfer de ejecución de la CPU para que los tiempos no se vean afectados por la ejecución fuera de servicio.
  • ejecute la ejecución rdtsc (lectura de marca de tiempo).Esto obtiene el número de ciclos de máquina ejecutados desde que se restableció el procesador.Este es un valor de 64 bits, por lo que con las velocidades actuales de la CPU se extenderá 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 colocan 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 brindarle 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 la hibernación es probablemente cierto, pero en la práctica probablemente no le importen los horarios a través de los límites de la hibernación.

  • con respecto al paso rápido:Las CPU de Intel más nuevas compensan los cambios de velocidad y devuelve un recuento ajustado.Hice un escaneo rápido sobre algunas de las cajas de nuestra red y encontré solo una caja que no la tenía:un Pentium 3 que ejecuta algún servidor de base de datos antiguo.(Estas son cajas de Linux, así que verifiqué con:grep constante_tsc /proc/cpuinfo)

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

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

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

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

lleva 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 cuál será la cantidad más pequeña que se incrementará.

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

Como han sugerido otras personas, gettimeofday() es malo porque configurar la hora puede provocar una desviación del reloj y alterar el cálculo. clock_gettime(CLOCK_MONOTONIC) es lo que quieres y clock_getres() te dirá la precisión de tu reloj.

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 normalmente está configurado en 100 Hz.En tales casos, la resolución temporal será menos precisa.

Obtuve esta respuesta de Temporizadores y medición de tiempo de alta resolución, Parte I

esta respuesta menciona problemas con el ajuste del reloj.Tanto sus problemas para garantizar las unidades de ticks como los problemas con el ajuste del tiempo se resuelven en C++ 11 con el <chrono> biblioteca.

El reloj std::chrono::steady_clock Se garantiza que no se ajustará y, además, avanzará a un ritmo 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 convirtiéndolas a una de las std::chrono::duration especializaciones, como std::chrono::microseconds.Con este tipo no hay ambigüedad sobre las unidades utilizadas por el valor del tick.Sin embargo, ten en cuenta que el reloj no necesariamente tiene esta resolución.Puede convertir una duración a attosegundos sin tener un reloj tan preciso.

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

La lectura del 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 probar 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 probablemente querrás comprobarlo. clock_getres(CLOCK_REALTIME) en su sistema para ver cuál es la resolución real.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top