API GetSystemTimeAsFileTime
обеспечивает доступ к настенным часам системы в формате времени файла.
64-битная структура FILETIME получает системное время как FILETIME в единицах 100 нс, срок действия которых истек с 1 января 1601 года. Для вызова GetSystemTimeAsFileTime
обычно требуется от 10 нс до 15 нс.
Чтобы исследовать реальную точность системного времени, предоставляемого этим API, необходимо обсудить детализацию, которая приходит вместе со значениями времени. Другими словами: как часто обновляется системное время? Первая оценка предоставляется скрытым вызовом API:
NTSTATUS NtQueryTimerResolution(OUT PULONG MinimumResolution,
OUT PULONG MaximumResolution,
OUT PULONG ActualResolution);
NtQueryTimerResolution
экспортируется собственной библиотекой Windows NT NTDLL.DLL. ActualResolution, сообщаемая этим вызовом, представляет период обновления системного времени в единицах 100 нс, который не обязательно соответствует периоду прерывания. Значение зависит от аппаратной платформы. Общие аппаратные платформы сообщают 156 250 или 100 144 для ActualResolution ; старые платформы могут сообщать о еще больших числах; более новые системы, особенно когда поддерживаются HPET
(высокоточный таймер событий) или constant/invariant TSC
, могут возвращать 156,001 для ActualResolution .
Это одно из сердцебиений, управляющих системой. MinimumResolution и ActualResolution относятся к конфигурации мультимедийного таймера.
ActualResolution можно установить с помощью вызова API
NTSTATUS NtSetTimerResolution(IN ULONG RequestedResolution,
IN BOOLEAN Set,
OUT PULONG ActualResolution);
или через интерфейс мультимедийного таймера
MMRESULT timeBeginPeriod(UINT uPeriod);
со значением uPeriod, полученным из диапазона, разрешенного
MMRESULT timeGetDevCaps(LPTIMECAPS ptc, UINT cbtc );
который заполняет структуру
typedef struct {
UINT wPeriodMin;
UINT wPeriodMax;
} TIMECAPS;
Типичные значения: 1 мс для wPeriodMin и 1 000 000 мс для wPeriodMax.
При поиске значений min / max здесь есть неудачное неверное толкование:
- wPeriodMin определяет минимальный период, который понятен в этом контексте.
- MinimumResolution , возвращаемое
NtQueryTimerResolution
, с другой стороны, указывает разрешение. Самое низкое достижимое разрешение ( MinimumResolution ) находится в диапазоне примерно до 20 мс, в то время как самое высокое достижимое разрешение ( MaximumResolution ) может составлять 0,5 мс. Тем не менее, 0,5 мс результат не доступен через timeBeginPeriod
.
Интерфейс мультимедийного таймера обрабатывает периоды, а NtQueryTimerResolution () - разрешения (обратное значение периода).
Резюме: GetSystemTimeAdjustment
- не та функция, на которую нужно смотреть. Эта функция только сообщает, как и если изменения времени сделаны. В зависимости от настройки интерфейса мультимедийного таймера timeBeginPeriod
ход времени может выполняться чаще и небольшими порциями. Используйте NtQueryTimerResolution
для получения фактического приращения времени. И имейте в виду, что настройка API мультимедийного таймера влияет на значения. (Пример: когда медиаплеер показывает видео, время становится коротким.)
Я диагностировал время окна в значительной степени. Некоторые результаты можно найти здесь .
Примечание: Настройка времени: 0.0156001 четко идентифицирует окна VISTA или выше с HPET
и / или constant/invariant TSC
в вашей системе.
Реализация: Если вы хотите поймать переход во времени:
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!
Но: Когда файловый переход очень короткий (например, установлен timeBeginPeriod(wPeriodMin)
), любой вывод, например fprintf
или std::cout
, может уничтожить результат, потому что задерживает цикл. В таких случаях я бы рекомендовал сохранить 20 результатов в структуре данных и сделать вывод впоследствии.
И: Переход времени файла не всегда может быть одинаковым. Вполне может быть, что приращение времени файла не совпадает с периодом обновления. См. Ссылку выше, чтобы получить более подробную информацию и примеры этого поведения.
Редактировать: Будьте осторожны при вызове timeBeginPeriod, поскольку частые вызовы могут значительно повлиять на системные часы MSDN . Это относится к версии Windows 7.
Звонки на timeBeginPeriod
/ timeEndPeriod
или NtSetTimerResolution
могут изменить системное время на целое ActualResolution .Это очень часто приводит к значительным изменениям системного времени.Однако, когда вызовы выполняются во время или около перехода системного времени, отклонения намного меньше.Опрос для перехода / приращения системного времени перед вызовами вышеуказанной функции рекомендуется для требовательных приложений, таких как клиенты NTP.Синхронизация с NTP-сервером затруднена, когда происходят нежелательные скачки в системных временных процессах.