Отметки времени микросекундного разрешения в Windows
Как получить метки времени с микросекундным разрешением в Windows?
Я ищу что-то лучшее, чем QueryPerformanceCounter
а также QueryPerformanceFrequency
(они могут дать вам только истекшее время с момента загрузки и не обязательно точны, если они вызываются в разных потоках - то есть QueryPerformanceCounter
может возвращать разные результаты на разных процессорах. Есть также некоторые процессоры, которые регулируют свою частоту для экономии энергии, что, очевидно, не всегда отражается на их QueryPerformanceFrequency
результат.)
Существует " Постоянно обновляемый поставщик времени с высоким разрешением" для Windows, но он не выглядит надежным. Когда микросекунды выглядят великолепно, они больше не доступны для скачивания.
Другой ресурс - получение точных временных меток под Windows XP, но он требует нескольких шагов, запуска вспомогательной программы, а также некоторых функций инициализации, я не уверен, работает ли она на нескольких процессорах.
Я также посмотрел на статью Википедии Time Stamp Counter, которая интересна, но не так полезна.
Если ответ только сделать это с BSD или Linux, это намного проще, и это хорошо, но я хотел бы подтвердить это и получить некоторое объяснение, почему это так сложно в Windows и так легко в Linux и BSD. Это то же самое прекрасное оборудование...
9 ответов
Я считаю, что это все еще полезно: Внутренние компоненты системы: Руководство по обеспечению поддержки мультимедийного таймера.
Он хорошо объясняет различные доступные таймеры и их ограничения. Возможно, ваше заклятое враг будет не столько разрешением, сколько задержкой.
QueryPerformanceCounter не всегда будет работать на скорости процессора. Фактически, он может попытаться избежать RDTSC, особенно в многопроцессорных (/ многоядерных) системах: он будет использовать HPET в Windows Vista и более поздних версиях, если таковые имеются, или таймер ACPI/PM. На моей системе (Windows 7 x64, двухъядерный AMD) таймер работает на частоте 14,31818 МГц.
То же самое верно для более ранних систем:
По умолчанию в Windows Server 2003 с пакетом обновления 2 (SP2) таймер PM используется для всех многопроцессорных APIC или ACPI HAL, если не завершится процесс проверки, чтобы определить, поддерживает ли BIOS APIC или ACPI HAL ".
Проблема в том, что проверка не проходит. Это просто означает, что ваш компьютер /BIOS поврежден. Затем вы можете либо исправить свой BIOS (рекомендуется), либо хотя бы переключиться на использование таймера ACPI (/usepmtimer) на данный момент.
Из C# легко - без P / Invoke - проверить поддержку таймера высокого разрешения с Stopwatch.IsHighResolution
а затем взглянуть на Stopwatch.Frequency
, Это сделает необходимый вызов QueryPerformanceCounter внутри.
Также учтите, что если таймеры сломаны, вся система будет разрушена и, в целом, будет вести себя странно, сообщая об отрицательных прошедших временах, замедляя работу и т. Д., А не только ваше приложение.
Это означает, что вы можете положиться на QueryPerformanceCounter.
... и вопреки распространенному мнению, QueryPerformanceFrequency()
msgstr "не может измениться во время работы системы".
Изменить: как документация QueryPerformanceCounter()
утверждает, что "не должно иметь значения, какой процессор называется" - и на самом деле весь хакерский процесс с привязкой к потоку необходим только в случае сбоя обнаружения APIC/ACPI и использования системой TSC. Это курорт, который не должен происходить. Если это происходит в более старых системах, вероятно, имеется обновление BIOS / исправление драйвера от производителя. Если нет, то /usepmtimer
переключатель загрузки все еще там. Если это также не удается, поскольку система не имеет надлежащего таймера, кроме Pentium TSC, вы можете рассмотреть возможность связывания с привязкой потоков - даже тогда пример, предоставленный другими в области "Содержимое сообщества" на странице, вводит в заблуждение, поскольку имеет незначительные накладные расходы из-за установки соответствия потоков при каждом вызове пуск / стоп - это вносит значительную задержку и, вероятно, уменьшает преимущества использования таймера с высоким разрешением.
Game Timing и Multicore Processors - это рекомендации по их правильному использованию. Пожалуйста, учтите, что ему уже пять лет, и в то время меньшее количество систем полностью поддерживало / поддерживало ACPI - вот почему при его создании в статье так подробно рассказывается о TSC и о том, как обойти его ограничения, сохраняя аффинность. нить.
Я считаю, что в настоящее время довольно сложно найти обычный ПК с нулевой поддержкой ACPI и без таймера PM. Наиболее распространенным случаем, вероятно, являются настройки BIOS, когда поддержка ACPI установлена неправильно (иногда, к сожалению, по умолчанию).
Анекдоты рассказывают, что восемь лет назад ситуация была иной в редких случаях. (Читает весело, разработчики работают над "недостатками" дизайна и избивают дизайнеров чипов. Если быть честным, то может быть так же, как и наоборот.:-)
QueryPerformanceCounter / QueryPerformanceFrequency, разрешение скорости процессора
Просто будьте осторожны с многопоточностью. Каждое ядро процессора может иметь свой собственный счетчик.
Некоторая дополнительная информация находится в Получение точных временных меток под Windows XP.
Если вам придется прибегнуть к этому методу:
Когда я пытался вручную записать данные в последовательный порт (для инфракрасного передатчика), я обнаружил, что установка максимального приоритета процесса и приоритета потока (в реальном времени) значительно повысила его надежность (без ошибок), это то, что пришлось если я помню, разрешение около 40 кГц, поэтому оно должно оставаться достаточно точным для разрешения в миллисекундах.
Я не думаю, что вы найдете решение лучше, чем QueryPerformanceCounter. Стандартный метод состоит в том, чтобы настроить ваш код на улавливание и отбрасывание скачков времени назад и массивных выбросов, которые могут возникнуть в результате переключения потоков процессорами. Если вы измеряете очень малые интервалы (если нет, то вам не нужна эта точность), то это не обычное явление. Просто сделайте это допустимой ошибкой, а не критической ошибкой.
В тех редких случаях, когда вам абсолютно необходимо быть уверенным в том, что этого никогда не произойдет, блокировка потоков путем установки маски соответствия процессоров является единственной возможностью.
Windows не является ОС реального времени.
Процессы в многозадачной ОС должны отдавать свое время другому потоку / процессу. Это дает некоторые накладные расходы на время.
Каждый вызов функции будет иметь накладные расходы, что дает небольшую задержку при возврате запроса.
Кроме того, вызывающему системному вызову потребуется, чтобы ваш процесс переключился из режима пространства пользователя в режим пространства ядра, который имеет относительно высокую задержку. Вы можете преодолеть это, запустив весь процесс в режиме ядра (например, код драйвера устройства).
Некоторые операционные системы, такие как Linux или BSD, лучше, но они все еще не могут поддерживать точное временное разрешение до микросекунды (например, точность nanosleep() в Linux составляет около 1 мс, не менее 1 мс), кроме вас подключите ядро к какому-то определенному планировщику, который даст преимущества вашему приложению.
Поэтому я думаю, что лучше адаптировать ваше приложение для решения этих проблем, например, часто перекалибровывая вашу процедуру синхронизации, что и обеспечивают ваши ссылки. AFAIK, самое высокое разрешение таймера для Windows по-прежнему равно GetPerformanceCounter/Frequency() независимо от его точности. Вы можете добиться большей точности, запустив подпрограмму таймера в отдельном потоке, установив привязку этого потока к одному базовому процессору и установив приоритет потока, который вы можете получить.
QueryPerformanceCounter является правильным решением для этого. Вопреки тому, что вы и некоторые люди, отвечающие вам, написали, этот вызов дает правильный ответ даже для многопроцессорных систем (если только рассматриваемая система не работает), и он обрабатывает даже изменение частоты процессора. В большинстве современных систем он получен из RDTSC, но обрабатывает все эти многопроцессорные и частотные изменения для вас. (Это значительно медленнее, чем RDTSC, хотя).
Посмотреть QueryPerformanceCounter
На многопроцессорном компьютере не должно иметь значения, какой процессор называется. Однако вы можете получить разные результаты на разных процессорах из-за ошибок в базовой системе ввода / вывода (BIOS) или на уровне аппаратной абстракции (HAL).
Пока в ответах много хорошей информации.
Если то, что вы ищете, - это простой способ получить время, прошедшее с 1 января 1970 года, с разрешением в миллисекундах или лучше в Windows XP или более поздней версии, то в CurrentTime.cpp из выпуска OSS от Apple есть очень простой кросс-платформенный пример этого. JavaScriptCore для MacOS 10.7.5(кажется, я не могу найти его в их версиях 10.8+). Код, на который я ссылаюсь, находится вCurrentTime()
функция.
Он использует стандартную технику использованияQueryPerformanceCounter()
рассчитать разницу во времени с разрешением выше миллисекунды, а затем периодически синхронизировать ее с системными часами, чтобы рассчитать временную метку и учесть дрейф часов. Для получения меток времени с более высоким разрешением требуется, чтобы вы работали под управлением Windows XP или более поздней версии, чтобыQueryPeformanceFrequency()
гарантированно преуспеют.
Он не учитывает переключатели контекста, слегка отбрасывающие вещи (как это делают "Реализация постоянно обновляющегося поставщика времени с высоким разрешением для Windows" и "Проект временной метки Windows"), но он постоянно выполняет повторную синхронизацию. Я бы не стал запускать ракету с ним, но примерно с 50 строками кода он прост в реализации и достаточно хорош для многих целей.
Кроме того, если вы знаете, что вы гарантированно используете Windows 8 / Windows Server 2012, вам просто нужно использоватьGetSystemTimePreciseAsFileTime()
, поскольку он возвращает системную дату и время с максимально возможной точностью (1 микросекунда или лучше).
Я обнаружил трудности с использованием PerformanceCounter
вместе с PerformanceCounterFrequency
потому что данный PerformanceCounterFrequency
отклоняется от фактической частоты.
Он отклоняется смещением, а также показывает тепловой дрейф. Новые аппаратные средства, кажется, имеют меньший дрейф, но дрейф и смещение довольно значительны. Дрейф в несколько частей на миллион уже повредит микросекундную точность в значительной степени, поскольку 1 часть на миллион составляет 1 мкс / с! Поэтому при использовании настоятельно рекомендуется использовать аппаратную калибровку. PerformanceCounter
с PerformanceCounterFrequency
, Это также может быть причиной того, что наблюдаются "сумасшедшие результаты", когда не часто вызывают определенные функции.
Я провел несколько более подробных исследований по этому вопросу. Описание можно найти в службах времени с микросекундным разрешением для Windows.
Я использовал класс DateTimePrecise из проекта кода.
Единственная проблема, с которой я столкнулся, заключается в том, что это дало бы сумасшедшие результаты, если бы я не вызывал его хотя бы каждые 10 секунд - я думаю, что было внутреннее целочисленное переполнение - поэтому у меня есть таймер, который выполняет DateTimePrecise.Now
каждые несколько секунд.
Вы также должны запустить NTP на машине, если хотите, чтобы время было точным.
Удачи...
Начиная с C++11 появился новый
<chrono>
заголовок, так что эта задача будет намного проще. Просто используйтеstd::chrono::high_resolution_clock
,std::chrono::system_clock
(настенные часы) илиstd::chrono::steady_clock
(монотонные часы) и у вас будет кроссплатформенное решение
auto start1 = std::chrono::high_resolution_clock::now();
auto start2 = std::chrono::system_clock::now();
auto start3 = std::chrono::steady_clock::now();
// do some work
auto end1 = std::chrono::high_resolution_clock::now();
auto end2 = std::chrono::system_clock::now();
auto end3 = std::chrono::steady_clock::now();
std::chrono::duration<long long, std::micro> diff1 = end1 - start1;
std::chrono::duration<double, std::nano> diff2 = end2 - start2;
auto diff3 = std::chrono::duration_cast<std::chrono::microseconds>(end3 - start3);
std::cout << diff.count() << ' ' << diff2.count() << ' ' << diff3.count() << '\n';
В С++ 17 и выше (или C11 и выше) есть другое решение:std::timespec_get()
#include <iostream>
#include <ctime>
int main()
{
std::timespec ts;
std::timespec_get(&ts, TIME_UTC);
char buf[100];
std::strftime(buf, sizeof buf, "%D %T", std::gmtime(&ts.tv_sec));
std::cout << "Current time: " << buf << '.' << ts.tv_nsec << " UTC\n";
}