Почему (или нет?) SFENCE + LFENCE эквивалентно MFENCE?

Как мы знаем из предыдущего ответа на вопрос: делает ли он какую-то смысл инструкцией LFENCE в процессорах x86 / x86_64? что мы не можем использовать SFENCE вместо MFENCE для последовательной согласованности.

Ответ там предполагает, что MFENCE = SFENCE + LFENCE , то есть LFENCE делает что-то, без которого мы не можем предоставить последовательную согласованность.

LFENCE делает невозможным переупорядочение:

 SFENCE LFENCE MOV reg, [addr] 

– To ->

 MOV reg, [addr] SFENCE LFENCE 

Например, переупорядочение MOV [addr], reg LFENCE -> LFENCE MOV [addr], reg обеспечивается механизмом – Store Buffer , который переупорядочивает Store-Loads для повышения производительности, и LFENCE не мешает ему. И SFENCE отключает этот механизм .

Какой механизм отключает LFENCE чтобы сделать невозможное переупорядочение (x86 не имеет механизма – Invalidate-Queue)?

И является ли переупорядочение SFENCE MOV reg, [addr] -> MOV reg, [addr] SFENCE возможно только в теории или, возможно, в реальности? И если это возможно, на самом деле, какие механизмы, как это работает?

SFENCE + LFENCE не блокирует переупорядочение StoreLoad, поэтому его недостаточно для последовательной согласованности . Это mfence только mfence (или операция lock или настоящая инструкция сериализации, такая как cpuid ). См. Переупорядочение памяти Джеффа Прешинга, занесенное в закон, для случая, когда достаточно полного барьера.


Из справочного руководства по sfence инструкций Intel для sfence :

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

но

Он не упорядочен по отношению к нагрузкам памяти или инструкции LFENCE.


LFENCE заставляет более ранние инструкции «заполнять локально» (то есть удаляться из нестандартной части ядра), но для хранилища или SFENCE, что просто означает помещение данных или маркера в буфер порядка памяти, а не очищение его так магазин становится глобально видимым. т.е. «завершение» SFENCE (выход из ROB) не включает очистку буфера хранилища.

Это похоже на то, что описано в разделе «Преширование» в «Блокировки памяти», как операции управления версиями, где барьеры StoreStore не являются «мгновенными». Позже в этой статье он объясняет, почему барьер #StoreStore + #LoadLoad + aLoadStore не соответствует барьеру #StoreLoad. (x86 LFENCE имеет некоторую дополнительную сериализацию streamа команд, но поскольку он не очищает буфер хранилища, аргументы все еще сохраняются).

LFENCE не полностью сериализуется, как cpuid ( который так же сильно mfence памятью как mfence или инструкция mfence ). Это просто LoadLoad + LoadStore барьер, а также некоторые элементы сериализации исполнения, которые, возможно, начинаются как детали реализации, но теперь закреплены в качестве гарантии, по крайней мере, на процессорах Intel. Это полезно с помощью rdtsc , а также для избежания спекуляции ветвей, чтобы смягчить Spectre.

BTW, SFENCE – это no-op, за исключением магазинов NT; он заказывает их в отношении обычных (выпущенных) магазинов. Но не в отношении нагрузок или LFENCE. Только на CPU, который обычно слабо упорядочен, барьер магазина-магазина делает что-либо.


Реальная проблема заключается в переупорядочении StoreLoad между магазином и загрузкой, а не между магазином и барьерами, поэтому вам следует посмотреть на корпус с магазином, затем на барьер, затем на загрузку .

 mov [var1], eax sfence lfence mov eax, [var2] 

могут стать глобально видимыми (т. е. зафиксировать кеш L1d) в следующем порядке:

 lfence mov eax, [var2] ; load stays after LFENCE mov [var1], eax ; store becomes globally visible before SFENCE sfence ; can reorder with LFENCE 

В общем, MFENCE! = SFENCE + LFENCE. Например, приведенный ниже код при компиляции с -DBROKEN не работает на некоторых системах Westmere и Sandy Bridge, но, похоже, работает над Ryzen. На самом деле в системах AMD достаточно СФЕРА, как представляется, достаточно.

 #include  #include  #include  #include  using namespace std; #define ITERATIONS (10000000) class minircu { public: minircu() : rv_(0), wv_(0) {} class lock_guard { minircu& _r; const std::size_t _id; public: lock_guard(minircu& r, std::size_t id) : _r(r), _id(id) { _r.rlock(_id); } ~lock_guard() { _r.runlock(_id); } }; void synchronize() { wv_.store(-1, std::memory_order_seq_cst); while(rv_.load(std::memory_order_relaxed) & wv_.load(std::memory_order_acquire)); } private: void rlock(std::size_t id) { rab_[id].store(1, std::memory_order_relaxed); #ifndef BROKEN __asm__ __volatile__ ("mfence;" : : : "memory"); #else __asm__ __volatile__ ("sfence; lfence;" : : : "memory"); #endif } void runlock(std::size_t id) { rab_[id].store(0, std::memory_order_release); wab_[id].store(0, std::memory_order_release); } union alignas(64) { std::atomic rv_; std::atomic rab_[8]; }; union alignas(8) { std::atomic wv_; std::atomic wab_[8]; }; }; minircu r; std::atomic shared_values[2]; std::atomic*> pvalue(shared_values); std::atomic total(0); void r_thread(std::size_t id) { uint64_t subtotal = 0; for(size_t i = 0; i < ITERATIONS; ++i) { minircu::lock_guard l(r, id); subtotal += (*pvalue).load(memory_order_acquire); } total += subtotal; } void wr_thread() { for (size_t i = 1; i < (ITERATIONS/10); ++i) { std::atomic* o = pvalue.load(memory_order_relaxed); std::atomic* p = shared_values + i % 2; p->store(1, memory_order_release); pvalue.store(p, memory_order_release); r.synchronize(); o->store(0, memory_order_relaxed); // should not be visible to readers } } int main(int argc, char* argv[]) { std::vector vec_thread; shared_values[0] = shared_values[1] = 1; std::size_t readers = (argc > 1) ? ::atoi(argv[1]) : 8; if (readers > 8) { std::cout << "maximum number of readers is " << 8 << std::endl; return 0; } else std::cout << readers << " readers" << std::endl; vec_thread.emplace_back( [=]() { wr_thread(); } ); for(size_t i = 0; i < readers; ++i) vec_thread.emplace_back( [=]() { r_thread(i); } ); for(auto &i: vec_thread) i.join(); std::cout << "total = " << total << ", expecting " << readers * ITERATIONS << std::endl; return 0; } 

Какой механизм отключает LFENCE, чтобы сделать невозможное переупорядочение (x86 не имеет механизма – Invalidate-Queue)?

В руководствах Intel, том 2A, стр. 3-464 документации для инструкции LFENCE :

LFENCE не выполняется до тех пор, пока все предыдущие инструкции не будут завершены локально, и никакая более поздняя инструкция не начнет выполнение до тех пор, пока LFENCE не завершится

Так что да, ваш пример переупорядочения явно запрещен инструкцией LFENCE . Второй пример, включающий только команды SFENCE является допустимым переупорядочением, поскольку SFENCE не влияет на операции загрузки.

  • __builtin_prefetch, сколько он читает?
  • Почему GCC использует умножение на странное число при реализации целочисленного деления?
  • Почему инструкции x86-64 на 32-разрядных регистрах обнуляют верхнюю часть полного 64-битного регистра?
  • Устанавливать все биты в регистре CPU на 1 эффективно
  • Оптимизация производительности сборки x86-64 - Выравнивание и outlookирование ветвлений
  • Ориентация как 32-битной, так и 64-битной с Visual Studio в том же решении / проекте
  • Приобретать / выпускать семантику с невременными магазинами на x64
  • 64-разрядный формат Mach-O не поддерживает 32-разрядные абсолютные адреса. Доступ к массиву NASM
  • x86_64 регистры rax / eax / ax / al, переписывающие содержимое полного регистра
  • Атомная двойная с плавающей запятой или векторная загрузка / сохранение SSE / AVX на x86_64
  • Какова цель инструкции LEA?
  • Interesting Posts

    jQuery ajax внутри проблемы цикла

    Почему эта строка xmlns: android = “http://schemas.android.com/apk/res/android” должна быть первой в XML-файле макета?

    Как отлаживать привязки событий JavaScript / jQuery к Firebug или аналогичным инструментам?

    Как найти индексы верхних 10 000 элементов в симметричной матрице (12k X 12k) в R

    Ошибка: «Исходное значение null не разрешено Access-Control-Allow-Origin» при загрузке XML-файла с помощью метода ajax JQuery

    Загрузка файла Symfony2

    Как вы используете соответствующий class цветов для текущей платформы?

    C # – WCF – межпроцессное взаимодействие

    Как написать новый символ строки в файл в Java

    Являются ли while (true) «петлями так плохо?

    Список стилей Gmail

    Повернуть поплавок в регулярную сетку предопределенных точек

    Оператор == несогласован в логических столбцах в data.table

    Как переместить профиль Chrome, а также открыть новые ссылки с перемещенным профилем?

    Уничтожьте жесткий диск без надлежащего оборудования

    Давайте будем гением компьютера.