temporizadores temporizador soft siemens semanal s_pulse logo c++ windows timing windows-7-x64

c++ - soft - temporizadores siemens



Funciones de temporización de Windows 7: cómo utilizar GetSystemTimeAdjustment correctamente? (3)

En realidad, estás haciendo un perfil de cuánto tarda un pase en el ciclo for (). Obtengo algo más de variabilidad, pero 5 milisegundos es lo correcto, la salida de la consola no es muy rápida. Agregue arbitrariamente algunas declaraciones std :: cout más para ralentizarlo.

GetSystemTimeAdjustment algunas pruebas con la función GetSystemTimeAdjustment en Windows 7 y obtuve algunos resultados interesantes que no puedo explicar. Tan pronto como lo entiendo, este método debería regresar si la hora del sistema se sincroniza periódicamente y, si es así, en qué intervalo y con qué incremento se actualiza ( consulte la función GetSystemTimeAdjustment en MSDN ).

A partir de esto, sigo que si GetSystemTimeAsFileTime la hora del sistema, por ejemplo, usando GetSystemTimeAsFileTime repetitivamente, no obtendría ningún cambio (el reloj del sistema no se ha actualizado), o un cambio que es un múltiplo del incremento recuperado por GetSystemTimeAdjustment . Primera pregunta: ¿Es correcta esta suposición?

Ahora considere el siguiente código de prueba:

#include <windows.h> #include <iostream> #include <iomanip> int main() { FILETIME fileStart; GetSystemTimeAsFileTime(&fileStart); ULARGE_INTEGER start; start.HighPart = fileStart.dwHighDateTime; start.LowPart = fileStart.dwLowDateTime; for (int i=20; i>0; --i) { FILETIME timeStamp1; ULARGE_INTEGER ts1; GetSystemTimeAsFileTime(&timeStamp1); ts1.HighPart = timeStamp1.dwHighDateTime; ts1.LowPart = timeStamp1.dwLowDateTime; std::cout << "Timestamp: " << std::setprecision(20) << (double)(ts1.QuadPart - start.QuadPart) / 10000000 << std::endl; } DWORD dwTimeAdjustment = 0, dwTimeIncrement = 0, dwClockTick; BOOL fAdjustmentDisabled = TRUE; GetSystemTimeAdjustment(&dwTimeAdjustment, &dwTimeIncrement, &fAdjustmentDisabled); std::cout << "/nTime Adjustment disabled: " << fAdjustmentDisabled << "/nTime Adjustment: " << (double)dwTimeAdjustment/10000000 << "/nTime Increment: " << (double)dwTimeIncrement/10000000 << std::endl; }

Toma 20 marcas de tiempo en un bucle y las imprime en la consola. Al final, imprime el incremento con el que se actualiza el reloj del sistema. Esperaría que las diferencias entre las marcas de tiempo impresas en el ciclo sean 0 o múltiplos de este incremento. Sin embargo, obtengo resultados como este:

Timestamp: 0 Timestamp: 0.0025000000000000001 Timestamp: 0.0074999999999999997 Timestamp: 0.01 Timestamp: 0.012500000000000001 Timestamp: 0.014999999999999999 Timestamp: 0.017500000000000002 Timestamp: 0.022499999999999999 Timestamp: 0.025000000000000001 Timestamp: 0.0275 Timestamp: 0.029999999999999999 Timestamp: 0.032500000000000001 Timestamp: 0.035000000000000003 Timestamp: 0.040000000000000001 Timestamp: 0.042500000000000003 Timestamp: 0.044999999999999998 Timestamp: 0.050000000000000003 Timestamp: 0.052499999999999998 Timestamp: 0.055 Timestamp: 0.057500000000000002 Time Adjustment disabled: 0 Time Adjustment: 0.0156001 Time Increment: 0.0156001

Por lo tanto, parece que la hora del sistema se actualiza utilizando un intervalo de aproximadamente 0,0025 segundos y no 0,0156 segundos como retorno por GetSystemTimeAdjustment .

Pregunta dos: ¿Cuál es el motivo de esto?


La resolución de GetSystemTimeAsFileTime depende del sistema. Si se ve, afirma que está entre 10 ms y 55 ms. Los comentaristas en el documento de MSDN lo ponen en 15 ms y "sub milisegundo". Lo que en realidad parece no está claro, pero nunca he visto su resolución reivindicada como igual a la precisión de 100 ns de la marca de tiempo.

Esto significa que siempre habrá alguna variación y también es la razón por la que las personas usan QueryPerformanceFrequency en su lugar.


La API GetSystemTimeAsFileTime proporciona acceso al reloj de pared del sistema en formato de hora de archivo.

Una estructura FILETIME de 64 bits recibe la hora del sistema como FILETIME en 100ns unidades, que han caducado desde el 1 de enero de 1601. La llamada a GetSystemTimeAsFileTime generalmente requiere de 10 ns a 15 ns.

Para investigar la precisión real del tiempo del sistema proporcionado por esta API, es necesario analizar la granularidad que acompaña a los valores de tiempo. En otras palabras: ¿con qué frecuencia se actualiza el tiempo del sistema? Una primera estimación es provista por la llamada API oculta:

NTSTATUS NtQueryTimerResolution(OUT PULONG MinimumResolution, OUT PULONG MaximumResolution, OUT PULONG ActualResolution);

NtQueryTimerResolution es exportado por la biblioteca nativa de Windows NT NTDLL.DLL. La resolución real informada por esta llamada representa el período de actualización de la hora del sistema en 100 unidades ns, que no coincide necesariamente con el período de interrupción. El valor depende de la plataforma de hardware. Las plataformas de hardware comunes informan 156,250 o 100,144 para ActualResolution ; las plataformas más antiguas pueden informar números aún mayores; Los sistemas más nuevos, especialmente cuando se HPET (High Precision Event Timer) o el constant/invariant TSC , pueden devolver 156,001 para la resolución real .

Este es uno de los latidos del corazón que controla el sistema. La resolución mínima y la resolución real son relevantes para la configuración del temporizador multimedia.

La resolución real se puede configurar usando la llamada API

NTSTATUS NtSetTimerResolution(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution);

o a través de la interfaz del temporizador multimedia

MMRESULT timeBeginPeriod(UINT uPeriod);

con el valor de uPeriod derivado del rango permitido por

MMRESULT timeGetDevCaps(LPTIMECAPS ptc, UINT cbtc );

que llena la estructura

typedef struct { UINT wPeriodMin; UINT wPeriodMax; } TIMECAPS;

Los valores típicos son 1 ms para wPeriodMin y 1,000,000 ms para wPeriodMax.

Hay una mala interpretación desafortunada al buscar los valores mínimos / máximos aquí:

  • wPeriodMin define el período mínimo, que es claro en este contexto.
  • MinimumResolution devuelto por NtQueryTimerResolution por otro lado especifica una resolución. La resolución más baja que se puede obtener ( MinimumResolution ) está en el rango de hasta aproximadamente 20 ms, mientras que la resolución más alta que se puede obtener ( MaximumResolution ) puede ser de 0,5 ms. Sin embargo, la consecuencia de 0.5 ms no es accesible a través de timeBeginPeriod .

La interfaz del temporizador multimedia maneja los períodos y NtQueryTimerResolution () maneja las resoluciones (valor recíproco del período).

Resumen: GetSystemTimeAdjustment no es la función a mirar. Esta función solo dice cómo y si se realizan cambios de tiempo. Dependiendo de la configuración de la interfaz del temporizador multimedia timeBeginPeriod , el progreso del tiempo se puede hacer con más frecuencia y en porciones más pequeñas. Use NtQueryTimerResolution para recibir el incremento de tiempo real. Y tenga en cuenta que la configuración de la API del temporizador multimedia influye en los valores. (Ejemplo: cuando el reproductor multimedia muestra un video, los tiempos son cortos).

He diagnosticado el tiempo de las ventanas en gran medida. Algunos de los resultados se pueden encontrar aquí .

Nota: Ajuste de tiempo: 0.0156001 identifica claramente Windows VISTA o superior con HPET y / o constant/invariant TSC en su sistema.

Implementación: si desea capturar la transición de tiempo:

FILETIME FileTime,LastFileTime; long long DueTime,LastTime; long FileTimeTransitionPeriod; GetSystemTimeAsFileTime(&FileTime); for (int i = 0; i < 20; i++) { LastFileTime.dwLowDateTime = FileTime.dwLowDateTime; while (FileTime.dwLowDateTime == LastFileTime.dwLowDateTime) GetSystemTimeAsFileTime(&FileTime); // enough to just look at the low part to catch the transition CopyMemory(&DueTime,&FileTime,sizeof(FILETIME)); CopyMemory(&LastTime,&LastFileTime,sizeof(FILETIME)); FileTimeTransitionPeriod = (long)(DueTime-LastTime); fprintf(stdout,"transition period: % 7.4lf ms)/n",(double)(FileTimeTransitionPeriod)/10000); } // WARNING: This code consumes 100% of the cpu for 20 file time increments. // At the standard file time increment of 15.625 ms this corresponds to 312.5ms!

Pero: cuando la transición del tiempo de archivo es muy corta (por ejemplo, establecida por timeBeginPeriod(wPeriodMin) ) cualquier salida como fprintf o std::cout podría destruir el resultado porque retrasa el ciclo. En tales casos, recomendaría almacenar los 20 resultados en una estructura de datos y luego realizar la salida.

Y: la transición de tiempo de archivo puede no ser siempre la misma. Es posible que el incremento de tiempo del archivo no coincida con el período de actualización. Vea el enlace de arriba para obtener más detalles y ejemplos de este comportamiento.

Editar: tenga cuidado al llamar a timeBeginPeriod, ya que las llamadas frecuentes pueden afectar significativamente el reloj del sistema MSDN . Este comportamiento se aplica hasta Windows versión 7.

Las llamadas a timeBeginPeriod / timeEndPeriod o NtSetTimerResolution pueden cambiar la hora del sistema tanto como ActualResolution . Hacerlo muy a menudo resulta en cambios considerables del tiempo del sistema. Sin embargo, cuando las llamadas se realizan en o cerca de la transición de la hora del sistema, las desviaciones son mucho menores. El sondeo de una transición / incremento de tiempo del sistema antes de las llamadas a la función anterior se recomienda para aplicaciones exigentes como clientes NTP. La sincronización con un servidor NTP es difícil cuando se producen saltos no deseados en el proceso del sistema.