TL; DR
rdtscp
и lfence/rdtsc
имеют одинаковые свойства последовательной восходящей сериализации на процессорах Intel. На процессорах AMD с сериализацией диспетчеризации lfence
обе последовательности также имеют одинаковые свойства сериализации в восходящем направлении. Что касается более поздних инструкций, rdtsc
в последовательности lfence/rdtsc
может отправляться для выполнения одновременно с более поздними инструкциями. Такое поведение может быть нежелательным, если вы также хотите точно рассчитать время этих последующих инструкций. Как правило, это не является проблемой, поскольку планировщик станции резервирования отдает приоритет более старым мопам для отправки, если нет структурных опасностей. После того, как lfence
выйдет на пенсию, rdtsc
мопов будут самыми старыми в РС, вероятно, без структурных опасностей, поэтому они будут немедленно отправлены (возможно, вместе с некоторыми более поздними мопами). Вы также можете поставить lfence
после rdtsc
.
В руководстве Intel V2 говорится о rdtscp
(выделено мое) следующее:
Инструкция RDTSCP не является командой сериализации, но она ожидает, пока все предыдущие инструкции не будут выполнены и все предыдущие нагрузки видны глобально. Но он не ждет, пока предыдущие хранилища будут видны глобально, и последующие инструкции могут начать выполнение до того, как операция чтения будет выполнена .
Часть «операция чтения» здесь относится к чтение счетчика меток времени. Это говорит о том, что rdtscp
внутренне работает как lfence
с последующим rdtsc
+ чтением IA32_TSC_AUX
. То есть сначала выполняется lfence
, затем выполняются две операции чтения из регистров (возможно, одновременно).
На большинстве процессоров Intel и AMD, поддерживающих эти инструкции, lfence/rdtsc
имеют немного больший размер количество мопов, чем rdtscp
. Число lfence
мопов, упомянутое в таблицах Агнера , относится к случаю, когда инструкции lfence
выполняются вплотную, что создает впечатление, что lfence
декодируется в меньшее число моп (1 или 2), чем то, во что фактически декодируется один lfence
(5 или 6 моп). Обычно lfence
используется без других последовательных lfence
с. Вот почему lfence/rdtsc
содержит больше мопов, чем rdtscp
. Таблицы Агнера также показывают, что на некоторых процессорах rdtsc
и rdtscp
имеют одинаковое количество мопов, что, я не уверен, является правильным. rdtscp
имеет больше смысла иметь один или несколько мопов, чем rdtsc
. Тем не менее, задержка может быть важнее, чем разница в количестве мопов, потому что именно это напрямую влияет на накладные расходы при измерении.
С точки зрения переносимости, rdtsc
старше rdtscp
; rdtsc
впервые был поддержан на процессорах Pentium, в то время как первые процессоры, поддерживающие rdtscp
, были выпущены в 2005-2006 годах (см .: Что такое тип процессора g cc, который включает поддержку RDTSCP? ). Но большинство используемых сегодня процессоров Intel и AMD поддерживают rdtscp
. Другое измерение для сравнения между двумя последовательностями состоит в том, что rdtscp
загрязняет еще один регистр (то есть ECX
), чем rdtsc
.
В итоге, если вам не важно читать IA32_TSC_AUX
MSR, нет особой причины, по которой вы должны выбирать одно из другого. Я бы использовал rdtscp
и отступил бы до lfence/rdtsc
(или lfence/rdtsc/lfence
) на процессорах, которые его не поддерживают. Если вам нужна максимальная точность синхронизации, используйте метод, описанный в Измерение задержки памяти со счетчиком отметок времени .
Как Андреас Абель указал , вам все еще нужно lfence
после последнего rdtsc(p)
, поскольку он не упорядочен по последующим инструкциям:
lfence lfence
rdtsc -- ALLOWED --> B
B rdtsc
rdtscp -- ALLOWED --> B
B rdtscp
Это также указано в руководствах .
Что касается использования rdtscp
, мне кажется правильным считать его компактным lfence + rdtsc
.
В руководствах используется различная терминология для двух инструкций (например, "выполнено локально" по сравнению с "глобально видимым" для нагрузок). ) но описанное поведение кажется таким же.
Я предполагаю, что так в оставшейся части этого ответа.
Однако rdtscp
- это одна инструкция, а lfence + rdtscp
- две, что делает часть lfence
в профилированном коде.
Да, lfence
должен быть легковесным с точки зрения ресурсов выполнения бэкэнда (это только маркер) он все еще занимает ресурсы переднего плана (два мопа?) и слот в ROB.
rdtscp
декодируется в большее количество мопов из-за его способности читать IA32_TSC_AUX
, так что пока он сохраняет ресурсы переднего плана (часть), занимает больше внутреннего.
Если чтение TS C выполняется сначала (или одновременно) с идентификатором процессора, то этот дополнительный моп актуален только для последующего кода .
Это может быть причиной того, что он используется в конце, но не в начале теста (где дополнительные мопы повлияют на код). Этого достаточно, чтобы сместить / усложнить некоторые микроархитектурные тесты.
Вы не можете избежать lfence
после и rdtsc(p)
, но вы можете избежать одного до с rdtscp
.
Это кажется ненужным для первого rdtsc
, так как предыдущий lfence
все равно не профилируется.
Еще одна причина использования rdtscp
в конце заключается в том, что он (согласно Intel) предназначен для обнаружения перехода на другой ЦП (поэтому он атомарно также загружает IA32_TSC_AUX
), поэтому в конце профилированного кода вы можете проверить, что код не был запланирован на другой процессор.
Программное обеспечение пользовательского режима может использовать RDTSCP, чтобы определить, произошла ли миграция ЦП между последовательными считываниями TS C.
Для этого, конечно, требуется чтение IA32_TSC_AUX
раньше (чтобы было с чем сравнивать), поэтому перед профилирующим кодом должен быть rdpid
или rdtscp
.
Если кто-то может позволить себе не использовать ecx
, первый rdtsc
может быть rdtscp
тоже (но см. выше), иначе (вместо сохранения идентификатора процессора в профилированном коде), сначала можно использовать rdpid
(таким образом, имея пару rdtsc + rdtscp
вокруг профилированного кода).
Это открыто для проблемы ABA , поэтому я не думаю, что у Intel есть сильная сторона в этом (если мы не ограничимся кодом, достаточно коротким, чтобы его можно было перепланировать не более одного раза).
РЕДАКТИРОВАТЬ Как указал PeterCordes, с точки зрения меры истекшего времени наличие миграции A-> B-> A не является проблемой, поскольку опорные часы такие же.
Дополнительная информация о том, почему rdtsc(p)
не полностью сериализуется: Почему RDTS C не является инструкцией сериализации? .