Red de conocimiento informático - Material del sitio web - Cómo escribir código para obtener el tiempo en microunidades en C

Cómo escribir código para obtener el tiempo en microunidades en C

Baidu, ctrlc, ctrlv. . .

c Método de temporización a nivel de milisegundos y microsegundos

En la plataforma Windows, hay dos temporizadores de uso común. Uno es el temporizador multimedia timeGetTime, que puede proporcionar temporización a nivel de milisegundos. Pero esta precisión sigue siendo demasiado aproximada para muchas aplicaciones. El otro es el contador QueryPerformanceCount, que puede proporcionar recuentos a nivel de microsegundos según el sistema. Para los programadores en procesamiento de gráficos en tiempo real, procesamiento de flujo de datos multimedia o construcción de sistemas en tiempo real, hacer un buen uso de QueryPerformanceCount/QueryPerformanceFrequency es una habilidad básica.

En las CPU Intel Pentium y superiores, hay un componente llamado "Marca de tiempo", que registra el tiempo desde que se encendió la CPU en el formato de un entero sin signo de 64 bits. transcurrido. Dado que la velocidad actual del reloj de la CPU es muy alta, este componente puede lograr una precisión de sincronización de nivel de nanosegundos. Esta precisión no tiene comparación con los dos métodos anteriores.

En las CPU superiores a Pentium, se proporciona una instrucción de máquina RDTSC (contador de marca de tiempo de lectura) para leer el número de esta marca de tiempo y guardarlo en el par de registros EDX:EAX. Dado que el par de registros EDX:EAX es el registro donde el lenguaje C guarda el valor de retorno de la función en la plataforma Win32, podemos considerar esta instrucción como una llamada de función ordinaria. Así:

inline unsigned __int64 GetCycleCount()

{

__asm ​​​​RDTSC

}

Pero no funciona, porque RDTSC no es compatible directamente con el ensamblador en línea de C, por lo que necesitamos usar la pseudoinstrucción _emit para incrustar directamente el código de máquina de la instrucción en formato 0X0F, 0X31, de la siguiente manera:

en línea sin firmar __int64 GetCycleCount()

{

__asm ​​​​_emit 0x0F

__asm ​​​​_emit 0x31

}

Cuando se necesite un contador en el futuro, puede, al igual que usar la API normal de Win32, llamar a la función GetCycleCount dos veces y comparar la diferencia entre los dos valores de retorno, así:

unsigned long t ;

t = (unsigned long)GetCycleCount ();

//Hacer algo que requiere mucho tiempo...

t -= (unsigned long)GetCycleCount ();

La ventaja de este método es:

1. Puede lograr directamente una precisión de sincronización de nivel de nanosegundos (cada ciclo de reloj es de un nanosegundo en una CPU de 1 GHz), lo cual es difícil de lograr con otros métodos de sincronización.

2. La función timeGetTime necesita vincularse a la biblioteca multimedia winmm.lib. Según las instrucciones de MSDN, la función QueryPerformance* requiere soporte de hardware (aunque no he visto una máquina que no la admita) y soporte de biblioteca KERNEL, por lo que ambos pueden. solo se puede utilizar en la plataforma Windows (para problemas de sincronización de alta precisión en la plataforma DOS, consulte la "Guía del desarrollador del programa de gráficos", que tiene instrucciones detalladas sobre cómo controlar el temporizador 8253).

Pero la instrucción RDTSC es una instrucción de CPU y es compatible con todas las máquinas Pentium y superiores bajo la plataforma i386, e incluso no hay restricciones de plataforma (creo que este método también es aplicable bajo la versión i386 de UNIX y Linux, pero las hay). no hay condiciones para las pruebas), y el costo de las llamadas a funciones es el más pequeño.

3. Tiene una relación de velocidad que corresponde directamente a la frecuencia de la CPU. Un conteo equivale a 1/(frecuencia de la CPU Hz) segundos, por lo que siempre que se conozca la frecuencia de la CPU, el tiempo se puede calcular directamente. Esto es diferente de QueryPerformanceCount, que necesita obtener el número de recuentos por segundo del contador actual a través de QueryPerformanceFrequency antes de convertirlo en tiempo.

Las desventajas de este método son:

1. La mayoría de los compiladores C/C existentes no admiten directamente el uso de instrucciones RDTSC y deben programarse incorporando directamente código de máquina. , que es más problemático.

2. La fluctuación de los datos es bastante grave. De hecho, para cualquier método de medición, la precisión y la estabilidad son siempre una contradicción. Si se utiliza timeGetTime de baja precisión para cronometrar, el resultado será básicamente el mismo cada vez, mientras que el resultado de la instrucción RDTSC será diferente cada vez, a menudo con cientos o incluso miles de diferencias. Esta es una contradicción inherente a la alta precisión de este enfoque.

Los siguientes son algunos pequeños ejemplos para comparar brevemente el uso y la precisión de los tres métodos de cronometraje

//Timer1.cpp Clase de temporizador usando la instrucción RDTSC//Definición de KTimer class Consulte "Programación de gráficos de Windows" P15

//Línea de compilación: CL Timer1.cpp /link USER32.lib

#include lt stdio.hgt; >#include "KTimer.h"

main()

{

t sin firmar

Temporizador KTimer

timer.Start();

Sueño(1000);

t = timer.Stop();

printf("Tiempo de duración: d \n", t);

}

//Timer2.cpp usa la función timeGetTime

//Necesita incluir lt; mmsys.hgt; , pero debido a la intrincada relación entre los archivos de encabezado de Windows

//Simplemente incluya lt; windows.hgt; bastante vago :)

//Línea de compilación: CL timer2.cpp /link winmm.lib

#include lt; windows.hgt;

#include lt; {

DWORD t1, t2;

t1 = tiempoGetTime();

Sueño(1000

t2 = tiempoGetTime(); ;

printf("Hora de inicio: u\n", t1

printf("Hora de finalización: u\n",

); printf("Tiempo de duración: u\n", (t2-t1));

}

//Timer3.cpp usa la función QueryPerformanceCounter

/ /Línea de compilación: CL timer3.cpp /link KERNEl32.lib

#include lt; windows.hgt

#include stdio.hgt; main()

{

LARGE_INTEGER t1, t2, tc

QueryPerformanceFrequency(amp;tc);

printf("Frecuencia" : u\n ", tc.QuadPart);

QueryPerformanceCounter(amp; t1);

Sleep(1000);

QueryPerformanceCounter(amp; t2) ;

printf("Hora de inicio: u\n", t1.QuadPart

printf("Hora de finalización: u\n", t2.QuadPart); >

printf("Tiempo de duración: u\n",( t2.QuadPart- t1.Qua

dParte));

}