Необходим ли mfence для rdtsc на платформе x86_64?
unsigned int lo = 0;
unsigned int hi = 0;
__asm__ __volatile__ (
"mfence;rdtsc" : "=a"(lo), "=d"(hi) : : "memory"
);
mfence
в приведенном выше коде это необходимо?
Исходя из моего теста, порядок процессора не найден.
Фрагмент тестового кода приведен ниже.
inline uint64_t clock_cycles() {
unsigned int lo = 0;
unsigned int hi = 0;
__asm__ __volatile__ (
"rdtsc" : "=a"(lo), "=d"(hi)
);
return ((uint64_t)hi << 32) | lo;
}
unsigned t1 = clock_cycles();
unsigned t2 = clock_cycles();
assert(t2 > t1);
2 ответа
Что нужно для разумного измерения rdtsc
это инструкция по сериализации.
Как известно, многие люди используют cpuid
до rdtsc
, rdtsc
необходимо сериализовать сверху и снизу (читай: все инструкции до того, как он должен быть удален, и он должен быть удален до того, как начнется тестовый код).
К сожалению, вторым условием часто пренебрегают, потому что cpuid
это очень плохой выбор для этой задачи (это затормаживает вывод rdtsc
).
При поиске альтернатив люди думают, что подойдут инструкции с "забором" в их именах, но это также не соответствует действительности. Прямо от Intel:
MFENCE не сериализует поток команд.
Инструкция, которая практически сериализована и подходит для любого измерения, когда предыдущие магазины не нужно заполнять, lfence
,
Проще говоря, lfence
гарантирует, что никакие новые инструкции не начнутся до того, как любая предыдущая инструкция завершится локально. Смотрите этот мой ответ для более подробного объяснения местности.
Это также не истощает Store Buffer, как mfence
делает и не забивает регистры как cpuid
делает.
Так lfence / rdtsc / lfence
это лучшая последовательность инструкций, чем mfence / rdtsc
, где mfence
в значительной степени бесполезен, если вы явно не хотите, чтобы предыдущие хранилища были завершены до начала / окончания теста (но не до rdstc
выполняется!).
Если ваш тест для обнаружения переупорядочения assert(t2 > t1)
тогда я верю, что вы ничего не испытаете.
Оставляя вне return
и вызов, который может или не может помешать процессору видеть второй rdtsc
во время переупорядочения, маловероятно (хотя возможно!), что ЦП переупорядочит два rdtsc
даже если один идет сразу за другим.
Представь, что у нас есть rdtsc2
это точно так же, как rdtsc
но пишет ecx:ebx
1
проведение
rdtsc
rdtsc2
весьма вероятно, что ecx:ebx > edx:eax
потому что у CPU нет причин выполнять rdtsc2
до rdtsc
,
Изменение порядка не означает случайное упорядочение, это означает поиск другой инструкции, если текущая инструкция не может быть выполнена.
Но rdtsc
не зависит от какой-либо предыдущей инструкции, поэтому вряд ли будет задерживаться при обнаружении ядром OoO.
Однако специфические внутренние микроархитектурные детали могут опровергнуть мой тезис, отсюда и вероятное слово в моем предыдущем утверждении.
1 Нам не нужна эта измененная инструкция: переименование регистра сделает это, но если вы не знакомы с ней, это поможет.
mfence предназначен для принудительной сериализации в процессоре перед rdtsc.
Обычно вы найдете там cpuid (который также является инструкцией сериализации).
Цитата из руководств Intel об использовании rdtsc прояснит ситуацию
Начиная с процессора Intel Pentium, большинство процессоров Intel поддерживают неупорядоченное выполнение кода. Цель состоит в том, чтобы оптимизировать штрафы из-за различных задержек инструкций. К сожалению, эта функция не гарантирует, что временная последовательность одиночных скомпилированных инструкций C будет соответствовать последовательности самой инструкции, записанной в исходном файле C. Когда мы вызываем инструкцию RDTSC, мы делаем вид, что эта инструкция будет выполнена точно в начале и в конце измеряемого кода (т. Е. Мы не хотим измерять скомпилированный код, выполняемый вне вызовов RDTSC или исполняемый между называет себя). Решение состоит в том, чтобы вызвать команду сериализации перед вызовом RDTSC. Инструкция сериализации - это инструкция, которая заставляет ЦП завершить каждую предыдущую инструкцию кода C перед продолжением выполнения программы. Тем самым мы гарантируем, что только выполняемый код будет выполняться между вызовами RDTSC и что никакая часть этого кода не будет выполняться вне вызовов.
TL; версия DR - без сериализации команды до rdtsc вы не представляете, когда эта команда начала выполняться, делая измерения, возможно, некорректными
СОВЕТ - используйте rdtscp, когда это возможно.
Исходя из моего теста, порядок процессора не найден.
До сих пор нет гарантии, что это может произойти - поэтому оригинальный код "memory"
чтобы указать возможный клоббер памяти, мешающий компилятору переупорядочить его.