Является ли барьер памяти инструкцией, которую выполняет процессор, или это просто маркер?

Пытаюсь понять, что такое барьер памяти. На основании того, что я знаю до сих пор, барьер памяти (например: mfence) используется для предотвращения переупорядочения инструкций от до к после и от после до до барьера памяти.

Это пример используемого барьера памяти:

instruction 1
instruction 2
instruction 3
mfence
instruction 4
instruction 5
instruction 6

Теперь мой вопрос: является ли инструкция mfence просто маркером, указывающим процессору, в каком порядке выполнять инструкции? Или это инструкция, которую ЦП фактически выполняет так же, как и другие инструкции (например: mov).


person Christopher    schedule 10.03.2017    source источник
comment
Это инструкция, которую выполняет ЦП, других инструкций нет.   -  person Ross Ridge    schedule 10.03.2017
comment
Обратите внимание, что барьеры памяти компилятора, такие как std::atomic_signal_fence() или GNU C asm("":::"memory"), являются просто маркерами в исходном коде и компилируются с нулевыми инструкциями. Они существуют для блокировки переупорядочения во время компиляции и особенно полезны, когда целевая архитектура имеет более сильную модель памяти, чем исходный язык (например, C ++ - ›x86 asm). preshing.com/20120625/memory-ordering-at-compile-time объясняет больше.   -  person Peter Cordes    schedule 11.05.2018
comment
Интересно, чего вы ждете от этой награды. Ваш ответ ясен. Если у вас есть дополнительные вопросы, обязательно сформулируйте их! Никто не может догадаться, какая часть ответа вас не устраивает.   -  person fuz    schedule 12.05.2018
comment
Обратите внимание, что вы устанавливаете, возможно, ложную дихотомию между инструкцией и маркером ». Почему не может быть и того, и другого? Да, это, несомненно, инструкция, но почему она не может быть инструкцией, которая в значительной степени служит маркером?   -  person BeeOnRope    schedule 12.05.2018


Ответы (4)


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

Вы можете ясно увидеть это как в Справочник по набору инструкций Intel и конкретная страница для защиты.

MFENCE. Выполняет операцию сериализации для всех инструкций загрузки из памяти и сохранения в память, которые были выданы до инструкции MFENCE. Эта операция сериализации гарантирует, что каждая инструкция загрузки и сохранения, которая предшествует инструкции MFENCE в программном порядке, становится глобально видимой перед любой инструкцией загрузки или сохранения, которая следует за инструкцией MFENCE.

Инструкция MFENCE упорядочена относительно всех инструкций загрузки и сохранения, других инструкций MFENCE, любых инструкций LFENCE и SFENCE и любых инструкций сериализации (таких как инструкция CPUID). MFENCE не сериализует поток инструкций. Слабоупорядоченные типы памяти могут использоваться для достижения более высокой производительности процессора с помощью таких методов, как выдача не по порядку, спекулятивное чтение, объединение записи и свертывание записи. Степень, в которой потребитель данных распознает или знает, что данные слабо упорядочены, зависит от приложений и может быть неизвестна производителю этих данных. Инструкция MFENCE обеспечивает эффективный с точки зрения производительности способ обеспечения упорядочения загрузки и сохранения между подпрограммами, которые производят неупорядоченные результаты, и подпрограммами, потребляющими эти данные.

Процессоры могут произвольно извлекать и кэшировать данные из областей системной памяти, которые используют типы памяти WB, WC и WT. Эта спекулятивная выборка может произойти в любое время и не связана с выполнением инструкции. Таким образом, он не упорядочен относительно выполнения инструкции MFENCE; данные могут быть помещены в кеши предположительно непосредственно перед, во время или после выполнения инструкции MFENCE.

Как вы можете видеть из отрывка, инструкция MFence выполняет довольно мало работы, а не просто является каким-то маркером.

person Johan    schedule 10.03.2017

Я объясню влияние, которое mfence оказывает на поток в конвейере. Рассмотрим, например, конвейер Skylake. Рассмотрим следующую последовательность инструкций:

inst1
store1
inst2
load1
inst3
mfence
inst4
store2
load2
inst5

Инструкции декодируются в последовательность операций в одном и том же программном порядке. Затем все ошибки передаются планировщику. Обычно без ограждений все мопы выдаются на исполнение вне очереди. Однако, когда планировщик получает mfence uop, он должен убедиться, что никакие мопы памяти ниже по потоку mfence не выполняются до тех пор, пока все восходящие мопы памяти не станут глобально видимыми (что означает, что хранилища удалены и загрузка по крайней мере завершена). Это относится ко всем доступам к памяти независимо от типа памяти области, к которой осуществляется доступ. Это может быть достигнуто либо планировщиком, чтобы он не выдавал какие-либо нижестоящие операторы хранилища или загрузку мопов в хранилище или загрузочные буферы, соответственно, до тех пор, пока буферы не будут исчерпаны, либо путем выдачи нижестоящих операций сохранения или загрузки и их маркировки, чтобы их можно было отличить от все существующие ошибки памяти в буферах. Все мопы, не связанные с памятью, выше или ниже забора могут выполняться вне очереди. В этом примере, когда store1 удаляется и load1 завершается (получая данные и удерживая их в каком-то внутреннем регистре), инструкция mfence считается завершенной. Я думаю, что mfence может или не может занимать какие-либо ресурсы в бэкэнде (ROB или RS), и он может быть переведен более чем на один uop.

У Intel есть патент, поданный в 1999 г., в котором описывается, как работает mfence. Поскольку это очень старый патент, реализация может быть изменена или может отличаться в разных процессорах. Я резюмирую здесь патент. mfence декодируется на три мопса. К сожалению, не совсем ясно, для чего используются эти мопы. Затем записи выделяются из станции резервирования, которая выделяется для хранения мопов, а также выделяется из буферов загрузки и хранения. Это означает, что буфер загрузки может содержать записи либо для истинных запросов нагрузки, либо для ограждений (которые в основном являются ложными запросами на загрузку). Точно так же буфер хранилища может содержать записи для истинных запросов хранилища и для ограждений. mfence uop не отправляется до тех пор, пока все предыдущие операции загрузки или сохранения (в соответствующих буферах) не будут удалены. Когда это происходит, сама mfence uop отправляется контроллеру кеш-памяти L1 как запрос памяти. Контроллер проверяет, выполнены ли все предыдущие запросы. В этом случае он будет просто обрабатываться как NOP, и uop будет удален из буферов. В противном случае контроллер кеша отклоняет mfence uop.

person Hadi Brais    schedule 11.05.2018
comment
Skylake декодирует mfence в 4 мопа (слитый и несвязанный), которые работают на p2 / p3 и p4 (AGU и порты хранения данных). agner.org/optimize. По крайней мере, на бумаге mfence не должен останавливать более поздние хранилища от выполнения (и помещения данных хранилища в буфер хранилища), он просто не позволяет этим хранилищам становиться глобально видимыми (фиксация в кеше L1d) впереди всего после забора. Однако mfence нужен механизм, чтобы остановить выполнение загрузок до того, как все предыдущие загрузки / сохранения станут глобально видимыми. Если реализация Intel более строгая, то это просто выбор дизайна. - person Peter Cordes; 12.05.2018
comment
@PeterCordes Вы знаете назначение каждого из этих 4-х мопов? Просто любопытно. - person Hadi Brais; 12.05.2018
comment
Нет, не знаю. Счетчики производительности могут сказать нам, на каких портах они работают, но все остальное - чистое предположение (или поиск патентов Intel или документов конференций). - person Peter Cordes; 12.05.2018
comment
@PeterCordes Мое лучшее предположение для sfence состоит в том, что его два мопа соответствуют «сложному AGU» мупу на p2 или p3 (p7 может выполнять только простые операции с адресом хранилища AGU, в то время как p2 и p3 идентичны, могут выполнять обе нагрузки и генерация адреса как для загрузки, так и для сохранения, а также поддержка сложных операций AGU), а также uop store или magic flush-store-buffer на p4 (порт хранения данных). Может быть, mfence строит поверх этой волшебной последовательности загрузка-хранилище-загрузка, нацеленную на один сложный магический адрес? - person Iwillnotexist Idonotexist; 12.05.2018
comment
@IwillnotexistIdonotexist: порт 2/3 также имеет блоки выполнения данных загрузки. В общих чертах, возможно, «заборщики» записывают маркеры в буфер порядка памяти (который загружается также), или, может быть, особый вид маркера, или просто делают что-то особенное с MOB. Случайное предположение, возможно, mfence мопы отключают порты загрузки (p2 / p3), и наличие дополнительного порта загрузки является причиной того, что mfence теперь 4 мупа, а не 3. Но это, вероятно, не так просто, потому что OoO exec отправляет в самые старые ready, а некоторые нагрузки старше mfence могут еще не иметь готовых адресных регистров. - person Peter Cordes; 12.05.2018
comment
@PeterCordes Вы куда-то идете. Если бы было всего 3 мопа, то можно было бы ожидать, что каждый мопс пойдет на каждый из портов p234. Но есть 4 упа. Они не отключают порты, как вы подозревали, по крайней мере, не в соответствии с патентом, на который я ссылался. Вместо этого логика MOB принимает регистр заказа, когда видит эти специальные мопы. Может случиться так, что даже если есть 3 порта, они могут быть 4 буферами загрузки / хранения в MOB, и поэтому каждый uop будет переходить в каждый буфер. - person Hadi Brais; 12.05.2018
comment
@HadiBrais: Я ввел mfence в цикл и посмотрел на распределение портов на SKL. В замкнутом цикле (times 4 mfence / dec ebx/jnz) я вижу (на mfence): 4 слитных домена (uops_issued.any:u) и 2 uops_executed.thread:u (незанятый домен, только пространство пользователя). (Всего для цикла выполнено / отправлено 9 операций, включая 1 объединенную ветвь макроса, которая выполняется на p6). p4: 1 муп на забор. p2: 0,625 мопса на забор p3: 0,375 мопса на забор. (другие порты незначительны, кроме p6 для петлевой ветви). Разделение 2,5 / 1,5 из 4 для p2 / p3 одинаково для всех прогонов без других загрузок / накоплений. - person Peter Cordes; 12.05.2018
comment
TL: DR: в таблице Агнера Тумана есть ошибка: это 4 слитных домена / 2 незанятых домена, поэтому 2 из этих проблем не нуждаются в порте выполнения. 2, которые работают на (p4) и (либо на p2, либо на p3). - person Peter Cordes; 12.05.2018
comment
@PeterCordes Ницца. Итак, 4 мопа занимают RS, но выдаются только два, не так ли? Можете ли вы также измерить MEM_INST_RETIRED.ALL_LOADS и MEM_INST_RETIRED.ALL_STORES? Я не уверен, есть ли событие перфорации памяти для всех операций с памятью. Было бы интересно узнать, влияет ли упор на забор на эти события производительности. - person Hadi Brais; 12.05.2018
comment
@HadiBrais: Это наоборот: 4 мопа (по терминологии Intel) выданы в ROB, но 2 из них (например, xor-zeroing) не нужно выполнять, поэтому, AFAIK, они вообще не попадают в RS. Два других нужно выполнить, и они выдаются / переименовываются в ROB и RS. mem_inst_retired.all_loads:u = 0,5 отсчета на mfence. (супер странно, но да, я уверен. 200M считаются для 100M loop iters с 4x mfence). mem_inst_retired.all_stores:u = 0 отсчетов. - person Peter Cordes; 12.05.2018
comment
@PeterCordes Классно, чувак. Можете ли вы поэкспериментировать с разным количеством ограждений в теле цикла? Как 1, 4, 8, 16, и измерьте те же счетчики производительности, что и выше. - person Hadi Brais; 12.05.2018
comment
Ха, хорошая идея, это не совсем постоянно! times 1 mfence: 0,498 + - 0,14% от all_loads на каждое ограждение. (но видел одну пробежку на 0.486). times 2 mfence: 0,497 + - 0,06%, но один рост составил 0,462 + - 7,9% (ocperf.py stat ... -r3). Может быть, один из этих прогонов конкурировал с другим гиперпотоком? times 4 mfence: 0,467 + - 2,1%, или 0,506 + - 3,46%. times 16 mfence: 0,4715, или другой рост на 0,497 + - 2,6%. Система в основном простаивала, но в фоновом режиме использовалось xmoto, использующее 4% ядра. У него не было фокуса, и его окно не было открыто. Никаких миграций ЦП для моих тестов. - person Peter Cordes; 12.05.2018
comment
С нагрузкой забора всегда в счет. С %rep 4 / mov eax, [rel buf] / mfence / %endrep: общее количество циклов составляет 36,3 центов на одно ограждение вместо 33,3 центов на одно ограждение для только площади. И mem_inst_retired.all_loads:u стабилизировался на уровне 2,00 + - 0,01% на пару mov+mfence, так что 1,0 на mfence. Кажется, что соседние заборы не всегда считаются нагрузкой. Но с store + mfence, у нас 41,3c / store + mfence, и снова есть некоторая изменчивость и половина количества загрузок: 0,450 + - 3,6% mem_inst_retired.all_loads:uvs. ровно 1 mem_inst_retired.all_stores:u на mov-store, без изменений для %rep 16. - person Peter Cordes; 12.05.2018
comment
упс - ›порты, похоже, не меняются для mfence при чередовании с mov нагрузкой. По-прежнему 4 слитых домена / 2 неслитных домена (p23 + p4). С нагрузкой в ​​петле mem_inst_retired.all_loads ~= dispatched.port2 + dispatched.port3, иначе нет. Есть много ошибок для счетчиков производительности; Я не проверял, есть ли в SKL какие-либо элементы, влияющие на mem_inst_retired.all_loads, но было бы ошибкой интерпретировать из этого слишком много смысла. - person Peter Cordes; 12.05.2018
comment
@PeterCordes Рассмотрите возможность размещения всех этих комментариев в качестве ответа либо на этот вопрос, либо на новый вопрос, отправленный любым из нас. Новый вопрос может быть примерно таким: «Какое влияние оказывает mfence на счетчики событий производительности памяти (отключенные нагрузки, попадания нагрузки L1 и пропуски загрузки L1)?». Было бы интересно узнать, как настроить эти счетчики, учитывая количество динамических ограждений в коде. - person Hadi Brais; 12.05.2018
comment
Наблюдаемые вами вариации в порядке. Я имею в виду, что они очень маленькие (‹5%) и на них можно не обращать внимания. - person Hadi Brais; 12.05.2018
comment
@PeterCordes Кстати, а разве теги memory-fences и memory-barriers не должны быть синонимами? - person Hadi Brais; 12.05.2018
comment
@HadiBrais: Да, теги должны быть синонимами, но у меня недостаточно очков ни в одном из тегов, чтобы даже назначить их дубликатами. Я пометил этот вопрос обоими, чтобы выделить избыточность, и, возможно, ее можно будет исправить. И, кстати, 5% вариация довольно велика за 100 миллионов итераций; обычно я ожидал бы как минимум на 2 порядка лучше, например как + - 0,05%, которые я получаю за cycles:u (такты пользовательского пространства). Если бы мы доверяли счетчику, это означало бы, что есть что-то странное / непоследовательное в том, как mfence ведет себя в разных микроархитектурных условиях. - person Peter Cordes; 13.05.2018
comment
Но да, разница, вероятно, просто в том, как подсчитывается счетчик в разных микроархитектурных условиях. Тем не менее, интересна четырехкратная разница в счетах с / без реальной нагрузки в контуре. Итак, mfence ~ = 0,5 mem_inst_retired.all_loads при отсутствии реальных нагрузок или 1,0 при смешивании с нагрузками. - person Peter Cordes; 13.05.2018
comment
@PeterCordes Вы можете проголосовать за предложение сделать их синонимами. Если мы соберем 4 голоса за, они станут синонимами. Любой, кто читает эти комментарии, может поддержать предложение синонима. Что касается вариаций, то, по моему опыту, многие, если не большинство счетчиков, демонстрируют такие вариации. В общем, у нас нет другого выбора, кроме как терпеть вариации при интерпретации счетчиков. - person Hadi Brais; 13.05.2018
comment
Ой, видимо, я мог бы предложить синонимы, просто я не нашел кнопку до конца справа внизу. stackoverflow.com/tags/memory-barriers/synonyms. Проголосовал за ваш. - person Peter Cordes; 13.05.2018
comment
Re: варианты: в микро -бенчмарках только с одним маленьким циклом, который никогда не выходит за пределы ядра (l1d совпадений), обычно изменение счетчиков является результатом фактического изменения в выполнении. Я использую ocperf.py stat в крошечном статическом исполняемом файле, который буквально делает это, а затем sys_exit, всего с 2 или 3 ошибками страницы и ничем иным, кроме вмешательства прерываний. Здесь нормальным является очень высокая точность / повторяемость / отношение сигнал / шум. например Может ли MOV для x86 действительно быть бесплатным? Почему я вообще не могу воспроизвести это? показывает лучше, чем 1 часть из 10 КБ, более 0,5 с. - person Peter Cordes; 13.05.2018
comment
@hadi - по моему опыту, большинство счетчиков не имеют такой необъяснимой дисперсии: большинство возвращают именно то количество событий, которое вы ожидаете (иногда вам нужно добавить несколько правил или исправлений, чтобы получить точное количество, иногда с точностью до 1). Это происходит из-за использования счетчиков на небольшом участке кода. Для больших выборок вы увидите вариации, но это, по-видимому, в значительной степени вызвано внешними событиями, такими как прерывания или вариациями, например, в путях кода вызова ядра, а не изменчивостью счетчика. - person BeeOnRope; 13.05.2018
comment
@BeeOnRope Я думаю, что преувеличил, когда сказал много, если не большинство. Это больше похоже на какие-то события на конкретных процессорах. Для некоторых счетчиков на определенных процессорах я видел отклонения до 40% в небольшом цикле! Сделать такие счетчики больше похожими на генераторы случайных чисел. Вот почему я думаю, что ‹5% отклонение, возможно, нормально, это не идеально, но просто нормально. - person Hadi Brais; 13.05.2018
comment
Обновление mfence uops vs. таблицы Агнера Фога: он, возможно, тестировал до того, как исправление микрокода для SKL079 сделало mfence дороже. См. Нижнюю часть страницы "Загрузка и сохранение" - единственные инструкции, которые меняются в порядке?, которую я недавно обновил после того, как наткнулся на объяснение того, почему mfence так дорого. Может быть, это разные мопы для реализации решения грубой силы с остановкой всего, но раньше было 4 мупа с неиспользуемыми доменами, чтобы только блокировать переупорядочение памяти. - person Peter Cordes; 12.07.2018
comment
@PeterCordes IIRC, тег memory-order должен относиться к моделям памяти ISA, а тег memory-model должен относиться к моделям памяти языка. Но кажется, что memory-order каким-то образом стал синонимом memory-barriers, но так быть не должно. Тег memory-barriers должен быть только синонимом memory-fences. Тег memory-order может быть синонимом memory-ordering. - person Hadi Brais; 09.02.2020

mfence - это инструкция.

Чтобы получить его в Linux:

1 / Напишите файл mfence.c

#include <stdio.h>

int main(){
    printf("Disass me\n");
    asm volatile ("mfence" ::: "memory");
    return 0;
}

2 / Компиляция

gcc mfence.c mfence

3 / разобрать

objdump -d mfence | grep -A 10 "<main>:"

000000000000063a <main>:
 63a:   55                      push   %rbp
 63b:   48 89 e5                mov    %rsp,%rbp
 63e:   48 8d 3d 9f 00 00 00    lea    0x9f(%rip),%rdi        # 6e4 <_IO_stdin_used+0x4>
 645:   e8 c6 fe ff ff          callq  510 <puts@plt>
 64a:   0f ae f0                mfence 
 64d:   b8 00 00 00 00          mov    $0x0,%eax
 652:   5d                      pop    %rbp
 653:   c3                      retq   
 654:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
 65b:   00 00 00 

4 / Обратите внимание, что в строке 64a mfence находится (3-битная) инструкция (0f ae f0)

Итак, это инструкция процессора (например, mov): процессор должен декодировать предыдущие инструкции, прежде чем перейти к ней, иначе он не сможет угадать выравнивание.

Например, 0f ae f0 может появиться в адресе, поэтому процессор не может использовать его в качестве производителя.

Наконец, это просто инструкция старой школы, и в точке ее выполнения в конвейере она будет синхронизировать доступ к памяти дальше в конвейере перед выполнением следующей инструкции.


Примечание: в Windows используйте макрос _ReadWriteBarrier in для создания mfence

person Tinmarino    schedule 12.05.2018

В вашем вопросе неверные предположения. MFENCE не препятствует изменению порядка инструкций (см. Выделенную цитату). Например, если есть поток из 1000 инструкций, которые работают только с регистрами, а инструкция MFENCE помещена в середину, то это не повлияет на то, как ЦП переупорядочивает эти инструкции.

Инструкция MFENCE упорядочена относительно всех инструкций загрузки и сохранения, других инструкций MFENCE, любых инструкций LFENCE и SFENCE и любых инструкций сериализации (таких как инструкция CPUID). MFENCE не сериализует поток инструкций.

Вместо этого инструкция MFENCE предотвращает переупорядочение загрузки и сохранения в кэш и основную память.

person Nam San    schedule 18.05.2018
comment
Я думаю, что x86 может объединять только соседние хранилища, потому что он должен фиксировать их в кеш L1d по порядку. (Модель памяти x86 не позволяет переупорядочивать StoreStore). Но я предполагаю, что это работает как пример чего-то другого, кроме блокировки более поздних загрузок (но не более поздних инструкций ALU) до тех пор, пока последнее хранилище перед MFENCE не станет глобально видимым. - person Peter Cordes; 18.05.2018
comment
@PeterCordes, ты прав. Я удалю последний абзац - person Nam San; 18.05.2018
comment
Хм, это была интересная часть ответа. У нас есть некоторые свидетельства того, что Skylake действительно объединяет соседние хранилища в одну строку кэша, но это трудно измерить, потому что пропускная способность порта хранилища составляет всего 1 за такт. В этом ответе не объясняется подробно, что значит остановить переупорядочение памяти, и вообще не упоминается буфер хранилища, что является ключом к недопониманию некоторых людей здесь (Действует ли барьер памяти одновременно как маркер и как инструкция? и Имеет ли процессор x86 инструкции по изменению порядка?) - person Peter Cordes; 18.05.2018