Pull to refresh

Comments 31

А для чего понадобилась собственная система измерения времени, почему не взять из существующих ОС? В Linux, например, есть стандартная функция POSIX.1-2001 clock_gettime(). Возвращает время с учётом наносекунд. Умеет работать с CLOCK_MONOTONIC. Т. к. POSIX, другие ОС тоже могут поддерживать эту функцию.
Вы статью-то дальше заголовка читали?
Для измерения задержек такого масштаба микросекундная точность уже недостаточна. Однако важна не только точность, но еще и накладные расходы на измерение времени. Линуксовый системный вызов clock_gettime() возвращает время с наносекундной точностью. На машине, которая вот прямо сейчас у меня под рукой (Intel® Xeon® CPU E5-2630 v2 @ 2.60GHz), этот вызов отрабатывает примерно за 120 нс. Очень неплохая цифра. К тому же clock_gettime() работает достаточно предсказуемо. Это позволяет учесть накладные расходы на его вызов и в действительности делать измерения с точностью порядка десятков наносекунд. Однако обратим теперь внимание вот на что. Чтобы измерить интервал времени, нужно сделать два таких вызова: в начале и в конце. Т.е. потратить 240 нс. Если измеряются плотно расположенные промежутки времени порядка 1-10мкс, то в некоторых таких случаях сам процесс измерения будет значительно искажать наблюдаемый процесс.
Прошу прощения. Просматривал кусками, но делал Ctrl+F, «linux», поэтому не нашёл. Не привык я, чтобы на русском сие название писали.

Собственно, это и есть ответ на мой вопрос, спасибо.
Интересно узнать, для каких практических задач автору понадобилась точность и дискретизация до наносекунд

Мотивация примерно следующий — чтобы смотреть, как оптимизация какой нибудь одной строчки кода влияет на количество тактов процессора.

Но такие измерения будут актуальны только для системы, на которой производятся эти измерения (в широком трактовании понимания системы, разумеется)
Автору не знаю, но от меня ему огромное спасибо, мне нужна в работе точность до микросекунды, но если есть возможность до наносекунд добиться точности — идеально. Буду пересматривать алгоритм с командой для работы с нашим акустическим оборудованием.
UFO just landed and posted this here
Более того, измерение серий ещё и ближе к реальному использованию. На практике чаще всего не важно, насколько быстро отработает данный конкретный код одноразово, но важно, чтобы миллион выполнений выполнился как-можно быстрее.

Единственное, что приходит в голову — какой-то очень высокочастотный трейдинг. Т.е. если код выполнился за 1 наносекунду, то надо поднять ставку на 1 цент, а если за 3 наносекунды — то уже на 3 цента.
Ну в теории оно может пригодиться во всяких микробенчмарках для тех, кто делает оптимизации для компилятора. Я таким в своем дипломе занимался, когда генетичекими алгоритмами оптимизировал байткод для джавы (даже статью на хабре писал).
Тут проблема в том, что если считать в цикле миллион вызовов какой-то инструкции, то сам цикл (увеличение счетчиков, проверка условий) может занимать больше времени, чем проверяемая инструкция, и в итоге очень сильно усложнять оценку результата. Вообще микробенчмаркинг та еще боль, так как на результаты начинают ощутимо влиять всякие случайные погрешности, запущенный на компьютере софт и ОС и чуть ли не погода на Марсе. Ну и неточные счетчики в том числе.
При этом выигрыш одной конкретной оптимизации действительно может быть микроскопическим (кто там будет считать нынче выигрыш от какой-нибудь классической замены mov eax 0 на xor eax eax?). Но множество различных подобных оптимизаций в масштабах целой программы уже дают значительный эффект.
Чтобы оптимизация какой-то индивидуальной строки которая сама по себе выполняется доли секунд приносила эффект, эта строка должна выполниться много раз а иначе откуда выигрышу взяться?
В этом случае если реализация самого цикла занимает настолько значительное время, что на фоне этого оптимизация кода который выполняется в цикле не приносит измеримого выигрыша, то какой собственно толк от такой оптимизации?
… аппаратура наряду со значением TSC может также предоставлять ID того CPU, на котором это значение прочитано (см. интеловскую инструкцию RDTSCP, «Intel 64 and IA-32 Architectures Software Developer's Manual», Volume 2)...

Какое-то странное пояснение. Как я понимаю, фишка RDTSCP — инвариантность:

...The invariant TSC will run at a constant rate in all ACPI P-, C-. and T-states. This is the architectural behavior moving forward. On processors with invariant TSC support, the OS may use the TSC for wall clock timer services (instead of ACPI or HPET timers)...

...The invariant TSC means that the TSC continues at a fixed rate regardless of the C-state or frequency of the processor...

кроме этого:

...The RDTSCP instruction waits until all previous instructions have been executed before reading the counter. However, subsequent instructions may begin execution before the read operation is performed...

в отличии от:

...The RDTSC instruction is not a serializing instruction. It does not necessarily wait until all previous instructions have been executed before reading the counter...
Возвращаемое значение в EDX:EAX там будет лежать тоже самое что и после RDTSC
Отличие как вы верно заметили в ожидании выполнения некоторых инструкций.
Плюс возвращается TSC_AUX, который согласно Intel-SDM:Chapter 17.17.2 позволит оценить разницу значений TSC разных CPU.



А что до инвариантности — либо TSC инвариантен в целом(CPUID.80000007H:EDX[8] == 1), либо ее нет в процессоре в принципе(SDM:Chapter:17.17.1).

А что до инвариантности — либо TSC инвариантен в целом(CPUID.80000007H:EDX[8] == 1), либо ее нет в процессоре в принципе(SDM:Chapter:17.17.1).

Кстати, посмотрел, на моем ноутбучном i3 2014 года этот бит стоит, т.е. у меня в системе инвариантный TSC.
UFO just landed and posted this here

А мне в голову еще один вариант пришел. Взять высокостабильный генератор, счетчик, возможно еще и МК. Правда, придется немного попаять.

Задача ради задачи? Извлечь значение с МК будет вечностью по меркам процессора. Команды старт-стоп будут запаздывать на микросекунды… так себе решение. И едва ли им измеришь выполнение кода на основном процессоре величиной в 20...50 инструкций, переключение контекста для передачи команд займет неймоверно много времени.
Высокостабильность генератора вообще перпендикулярное требование, оно может накладываться на задачу а может быть не важным. Более того, высокостабильные генераторы обычно очень прихотливы и долго выходят на режим — например такой генератор как ГИАЦИНТ-М, выходит на рабочий режим ЧАСЫ!

Поскольку автор не указал под какие задачи он делал данное решение, но указал, использует его в виде секундомера, то вероятнее всего это измерение каких-то быстропротекающих процессов. Обычно такие процессы не нужно измерять каждый цикл, достаточно нескольких измерений для усреднения значения. Поэтому предложенное мной решение вполне подходит.

Измерять эти процессы где-то снаружи процессора не выйдет — периферия настолько медленная что одна только передача сигнала до процессора исказит результат. Если такая задача возникла и было выбрано такое решение — значит цель измерения лежит внутри процессора. Тут уже никакие внешние аппаратные приблуды, и уж темболее контроллер не поможет(разрешение в 1нс соответствует частоте счета 1ГГц, такие счетчики ещё поискать не говоря о контроллерах общего назначения).
Ну прям так сразу любая перефирия медленная? Например, в DDR4 RAM один цикл меньше наносекунды и она не находится на процессоре, так что сигнал от процессора не так уж и искажает результат, если конечно вы не хотите сразу этот сигнал на механическом диске зафиксировать.
Это только в BURST-режиме, т.е. в потоке данных. От команды процессора до реальной выборки из памяти проходит довольно много времени по меркам циклов работы с памятью. Причем из-за работы механизма кеширования это время ещё и не постоянное. Поинтересуйтесь что такое тайминг памяти и какое значение характерно для DDR4 — если память мне не изменяет это порядка 15 тактов на одну выборку. Но за счет конвееризации 15 тактов уходит только на выборку первой порции данных, а дальше идет по такту на каждую порцию(64 или 128 бит). Таким образом хоть память и обеспечивает высокую скорость работы с отзывчивостью(временем доступа) у неё не так хорошо.
Я не говорю о выборке из памяти, я говорю о времени, которое нужно чтобы послать команду на перефирию и времени, которое перефирии нужно чтобы принять команду, чего достаточно, чтобы зафиксировать момент времени. Я не слова ни сказал о пропускной способности памяти или времени, которое требуется чтобы передать все команды необходимые протоколом DDR и вернуть назад прочитанные данные — коммуникация с целью зафиксировать точку во времени могут быть и проще чем полноценное чтение/запись в память. Не нужно мешать все в одну кучу.
Интересно. Именно из-за проблемы рассинхронизации счетчиков на разных ядрах лет 5 назад у меня переставал работать пульт от ТВ-тюнера через пол часа работы компьютера. Оказалось, что это была проблема исключительно для AMD-процессоров. А я-то думал что пульт обрабатывается в тюнере аппаратно…
UFO just landed and posted this here
Я в какой-то момент окунулся в этот процесс высокоточных измерений, но умный человек посоветовал использовать HPET и не париться, точность у меня получилась порядка 600нс и она была ограничена временем доступа к HPET. Использовать RDTSC я не стал из-за того, что на разных ядрах у него ТОЧНО разные значения и частота его инкрементации зависит от частоты процессора. А так как все процессоры имеют авторазгон и прочие возможности поменять в динамике частоту, преобразование тики -> секунды стало не тривиальным.
Для тех кому интересно зачем это надо: современная высокоскоростная периферия требует таких скоростей, ты обязан подать или считать данные через малый интервал времени, а Винда может отсечь не менее 15 микросекунд с вероятностью что это значение сильно возрастет.
Использовать RDTSC я не стал из-за того, что на разных ядрах у него ТОЧНО разные значения и частота его инкрементации зависит от частоты процессора

Я выше в комментарии уже упомянул, что есть RDTSCP — инвариантная версия RDTSC, у которой такой зависимости нет. Так же, RDTSCP выполнится не раньше, чем все предыдущие команды, стоящие до него.

Только в статье про это почему-то не написано.
Я когда этим занимался не знал о такой инструкции, да и узнал только из Вашего комментария. Но она как-то решает проблему изменения частоты ядер?
Но она как-то решает проблему изменения частоты ядер?

Извиняюсь, ввел в заблуждение.

RDTSCP — это выполнение LFENCE (или в какой-то мере аналог) + чтение TSC (то же, что дает RDTSC) + чтение ID

И инвариантный TSC — это отдельная фича, проверяется CPUID.80000007H:EDX[8].

Если эта фича есть, то что RDTSC, что RDTSCP дают значения, которые не зависят от изменения частоты ядер.
Аппаратный метод для тех случаев когда время выполнения нужно «потом»:

В процессорах Intel (начиная со Skylake и Goldmont) есть технология Processor Trace. Аппаратная фича для записи всего потока выполения приложения, в том числе с временными метками.

Без инструментации приложения, с помощью simple-pt собирается трасса (главное не забыть включить генерацию CYC пакетов) и с минимальным оверхедом в рантайме, можно получать точность в единицы клоктиков процессора для линейного блока кода (итерации цикла например). Linux perf тоже умеет собирать PT, но там нет поддержки CYC-пакетов.

Для пересчета тиков в секунды можно попробовать всё-таки использовать FPU, только вместо деления делать умножение на обратную величину. (Будем считать, что перекалибровка частоты, после которой нужно делить, происходит очень редко.) То есть на критическом участке кода имеем одно FPU-умножение (обычно 3 такта), конверсию integer -> float и обратно (еще два раза примерно по одному такту).
Sign up to leave a comment.

Articles