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