The TSC is generally readable via the RDTSC instruction from user mode, making it the fastest, easiest, and most precise time base available on modern machines.
На мой взгляд, само получение значения tsc затратит меньше процессорных тактов и данные получатся точнее (как вместо sleep я использовал NtDelayExecution. Еще мелочь, но QueryPerformanceCounter возвратил мне 0 на системе PIII с win2k, а tsc — нормальное значение.
Хотя, я не против, конечно QueryPerformanceCounter, QueryPerformanceFrequency также вполне можно использовать.
Я еще поэкспериментирую с применением этих функций.
пока у вашей операционной системы не появится real-time scheduler говорить о каких бы то ни было точных измерениях времени бесполезно… thread/process switch отжирает драгоценное время, обработка прерываний происходит в ущерб программам уровня пользователя. опираться на полученные результаты нет смысла. единственное, что гарантируют все эти функции — процесс проспит не меньше указанного интервала времени. под различной нагрузкой в реальных приложениях все это треснет по швам к сожалению
Дополнительная информация:
1. На некоторых процессорах значение RDTSC может быть рассинхронизировано для разных ядер. Вы никогда не знаете, на каком именно ядре в данный момент выполняется Ваша программа (если только не выставили affinity mask, да и то, я бы не был на 100% уверен), поэтому чтение TSC процессорной командой иногда само вносит существенные искажения. В SMP системах RDTSC вообще по определению рассинхронизировано между процессорами. Поэтому думаю, что performance counters лучший метод для точных замеров времени в прикладных программах для Windows.
2. Программу для Windows не стоит писать так, чтобы она зависела от задержек в функциях типа Sleep. Если речь идет об управлении устройством в реальном времени, то нужно выносить все это в драйвер. В других случаях использовать мультимедийные функции для таймирования. А если кто-то пишет часы для desktop-а – вероятно лучше всего подойдут waitable timers.
Используя для оптимизации кода профайлер из состава библиотеки Jedi, позволил себе исследовать его с целью определения не только общего времени выполнения блока кода, количества запусков и, соответственно, среднего времени операции, но и максимального времени выполнения одиночной операции. Обнаружилось нечто интересное — для блоков кода со средним временем выполнения менее 1 мс значение максимального времени давало часто 16 мс, а то и 32. Особенно когда общее время выполнения при сотнях и тысячах запусков как раз получается те же 16 или 32 мс.
В том профайлере для замера временных отсечек используется функция GetTickCount. Не подскажете, чем заменить ее для более точного отсчета малых интервалов?
denim, все так, но описанное хотя бы чуть-чуть поднимает и пробует решить проблему…
sysprg, к сожалению не было возможности проверить и убедиться в преимуществах QueryPerformanceCounter на многоядерных системах, но судя по сказанному Вами, его действительно лучше использовать. Прежде всего я искал способ получить задержки, и для эксперимента, в котором RDTSC служил для подсчета, он пока мне сгодился.
А задержки эти, увы с нужным интервалом не получил: тоже верно — нужен драйвер. Хотя если задержки до 1 мс, можно обойтись, как я описал (NtDelayExecution, sleep, таймеры).
Даже если выставить real-time приоритет для thread, то все равно из-за подкачки/откачки страниц или еще каких-то влияний ядро Windows не всегда мгновенно активирует пользовательскую программу, поэтому писать на прикладном уровне для Windows что-то зависящее от задержек в 1 миллисекунду и меньше — вообще неправильно и малореально. Как и для Linux. С такими вещами нужно или на уровне ядра работать, или использовать специализированные системы реального времени. Как минимум в Windows или в Linux нужно выключать своппинг и не запускать в параллель всякие мультимедийные программы. Но это уже превращает программу в пригодную к использованию только на выделенной машине. Вывод — не очень подходят ОС общего назначения для этого, на прикладном уровне…
В любом случае спасибо за интересную статью! Можно убрать накопление погрешности, это да. Но нельзя обеспечить четкую активацию в точное время с точностью до миллисекунд. Если Вы запустите Ваши программы с любым методом точных задержек параллельно с какой-нибудь ресурсо-жрущей игрушкой, то Вы увидите, что какой бы Вы не использовали метод задержки и какой бы приоритет для thread не выбрали, но время от времени будут происходить задержки в активации thread более чем на одну миллисекунду из-за свопинга или из-за того, что некоторые directx операции не прерываются ядром, и обеспечить мгновенную активацию прикладной программы для Windows в суб-миллисекундном диапазоне в общем случае невозможно.
всё зависит от конечной потребности;
иногда бывает что счёт идёт на такты, но rdtsc особо верить нельзя ± 50-100 тактов(pipe-line, out-of-order execution);
но да почти всегда достаточно и QueryPreformanceCounter/QueryPerformanceFrequency, которые работают через ZwQueryPerformanceCounter;
Методология измерения не совсем правильная. NtDelayExecution переносит поток из списка плнируемых в список ожидания. Повторный запуск произойдет через заданный интервал + время до следующего планирования + время на смену контектов потока. Поэтому маленькие интервалы никогда не будут правильно измеряны в ОС с планировщиками общего назначения. А при больших интервалах кажущаяся точность появляется потому, что отношение необходимой задержки к накладным расходам становится больше — накладные расходы меньше влияют на результат измерения. При переключении задач может также происходить инвалидация кеша процессора, его заполнение требует время, вероятность велика, если код долго не получал управления из-за того, что был отложен — скорее всего неиспользуемый код был вытеснен.
rdtsc также не является реальным показателем, как уже говорили про разные ядра и процессоры также стоит отнести регуляцию частоты в мобильных решениях. Также, с появлением переупорядочивания на конвеере в процессорах Интел не гарантируется, что rdtsc исполнится не раньше, чем предшествующие инструкции.
Таким образом, чтобы точно измерить интервал, надо не выходить в ожидание, а наоборот занять процессор полностью, время от врмени получая метки времени, желательно как-то избегать переключения задач или учесть накладные расходы на переключение — для этого необходимо будет найти способ определения моментов переключения контекстов, Clerk на васме писал про некоторые фичи Windows, которые позволяют засечь момент переключения контекстов.
Также желательно инвалидировать кеши 1 уровня перед замерами и очищать конвеер (например дальним вызовом, только тут может помешать предсказание переходов...).
1. «На некоторых версиях Delphi есть готовая команда rdtsc»
Это не готовая команда на некоторых версиях Delphi, а наличие поддержки встроенным ассемблером оной команды. В Delphi6 уже есть, в старших тем более.
2. Про разные архитектуры процессоров, многоядерность и нереалтаймность Win32 уже сказали.
3. А какое практическое применение у этого метода? Т.е. когда он действительно дает принципиально лучший результат относительно известных стандартных? Ведь если мы имеем дело с получением некоторых отсчетов от некоторого железа, то удобнее всего (и так делается в реальной жизни) использовать метки времени от самого железа.
НЛО прилетело и опубликовало эту надпись здесьНЛО прилетело и опубликовало эту надпись здесь
Точное время: измеряем, применяем