"cpuid" перед "rdtsc"
Иногда я сталкиваюсь с кодом, который читает TSC с инструкцией rdtsc
, но вызывает cpuid
раньше.
Почему требуется вызов cpuid
? Я понимаю, что это может иметь какое-то отношение к другим ядрам, имеющим значения TSC, но что именно происходит, когда вы вызываете эти две инструкции последовательно?
Ответы
Ответ 1
Это, чтобы предотвратить внеочередное исполнение. По ссылке, которая теперь исчезла из Интернета (но была случайно скопирована здесь до ее исчезновения), этот текст взят из статьи под названием "Мониторинг производительности" одного из авторов Джона Экердала:
Процессоры Pentium Pro и Pentium II, поддерживающие внеплановые инструкции исполнения, могут выполняться в другом порядке, как вы их запрограммировали. Это может быть источником ошибок, если не позаботиться о.
Чтобы предотвратить это, программист должен сериализовать очередь команд. Это можно сделать, вставив команду сериализации, например инструкцию CPUID, перед командой RDTSC.
Ответ 2
Две причины:
- Как говорит paxdiablo, когда CPU видит код операции CPUID, он проверяет выполнение всех предыдущих инструкций, а затем - CPUID перед выполнением любых последующих инструкций. Без такой инструкции конвейер выполнения ЦП может в конечном итоге выполнить TSC раньше, чем инструкция (ы), которую вы хотели бы рассчитать.
- Значительная часть машин не может синхронизировать регистры TSC по ядрам. Если вы хотите прочитать это изо рта лошади - вырубите себя по адресу http://msdn.microsoft.com/en-us/library/ee417693%28VS.85%29.aspx. Таким образом, при измерении интервала между показаниями TSC, если только они не взяты на одном и том же ядре, у вас будет фактически введенный случайный, но, возможно, постоянный (см. Ниже) интервал - он может легко составить несколько секунд (да секунд) даже вскоре после загрузки, Это эффективно отражает то, как долго BIOS работал на одном ядре, прежде чем запускать другие, плюс - если у вас есть какие-либо неприятные варианты энергосбережения - увеличение дрейфа, вызванного ядрами, работающими на разных частотах или выключающимися снова. Итак, если вы не прибили потоки, считывающие регистры TSC, к одному и тому же ядру, то вам нужно будет создать некую межъядерную дельта-таблицу и знать идентификатор ядра (который возвращается CPUID) каждого образца TSC по порядку. чтобы компенсировать это смещение. Это еще одна причина, по которой вы можете видеть CPUID вместе с RDTSC, и действительно причина, почему с более новой версией RDTSCP многие ОС хранят номера идентификаторов ядра в возвращаемых дополнительных данных TSC_AUX [31: 0]. (Доступный из Core i7 и Athlon 64 X2, RDTSCP является гораздо лучшим вариантом во всех отношениях - ОС обычно дает вам идентификатор ядра, как уже упоминалось, атомарный для чтения TSC и предотвращает переупорядочение команд).
Ответ 3
CPUID является сериализацией, предотвращая неупорядоченное выполнение RDTSC.
В эти дни вы можете смело использовать LFENCE. Он задокументирован как сериализация в потоке команд (но не в памяти) на процессорах Intel, а теперь и на AMD после их обновления микрокода для Spectre.
https://hadibrais.wordpress.com/2018/05/14/the-significance-of-the-x86-lfence-instruction/ объясняет больше о LFENCE.
См. Также https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/ia-32-ia-64-benchmark-code-execution-paper.pdf для получения более подробной информации. использовать RDTSC P, который удерживает CPUID (или LFENCE) вне временной области:
LFENCE ; (or CPUID) Don't start the timed region until everything above has executed
RDTSC ; EDX:EAX = timestamp
mov ebx, eax ; low 32 bits of start time
code under test
RDTSCP ; built-in one way barrier stops it from running early
LFENCE ; (or CPUID) still use a barrier after to prevent anything weird
sub eax, ebx ; low 32 bits of end-start
Смотрите также Получить количество циклов ЦП? для получения дополнительной информации о предостережениях RDTSC, таких как constant_tsc и nonstop_tsc.
В качестве бонуса RDTSCP дает вам основной идентификатор. Вы также можете использовать RDTSCP для времени запуска, если хотите проверить миграцию ядра. Но если ваш процессор имеет функции constant_tsc
, все ядра в пакете должны синхронизироваться с TSC, так что на современном x86 это обычно не требуется.
Вместо этого вы можете получить идентификатор ядра из CPUID, как указывает ответ @Tony.