Необходим ли 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" чтобы указать возможный клоббер памяти, мешающий компилятору переупорядочить его.

Другие вопросы по тегам