Что является противоположностью «полного барьера памяти»? - PullRequest
0 голосов
/ 24 июня 2018

Иногда я вижу термин «полный барьер памяти», используемый в руководствах по упорядочению памяти, что, как мне кажется, означает следующее:

Если у нас есть следующие инструкции:

instruction 1
full_memory_barrier
instruction 2

Тогдаinstruction 1 не допускается переупорядочение ниже full_memory_barrier, а instruction 2 не допускается переупорядочение выше full_memory_barrier.


Но что противоположно полному барьеру памятиЯ имею в виду, существует ли что-то вроде «полупрозрачного барьера», который не позволяет процессору переупорядочивать инструкции в одном направлении?

Если существует такой барьер памяти, я не вижу его смысла, я имею в виду, еслиу нас есть следующие инструкции:

instruction 1
memory_barrier_below_to_above
instruction 2

Предположим, что memory_barrier_below_to_above является барьером памяти, который не позволяет переупорядочить instruction 2 выше memory_barrier_below_to_above, поэтому следующее не будет разрешено:

instruction 2
instruction 1
memory_barrier_below_to_above

Но допустимо следующее (что делает этот тип барьера памяти бессмысленным):

memory_barrier_below_to_above
instruction 2
instruction 1

1 Ответ

0 голосов
/ 25 июня 2018

http://preshing.com/20120710/memory-barriers-are-like-source-control-operations/ объясняет различные виды барьеров, таких как LoadLoad или StoreStore.Барьер StoreStore только предотвращает переупорядочение магазинов через барьер, но загрузка все равно может выполняться не по порядку.

На реальных процессорах любые барьеры, которые включают StoreLoad, также блокируют все остальное и поэтому называются «полными барьерами»,StoreLoad - самый дорогой тип, потому что это означает опустошение буфера хранилища, прежде чем последующие загрузки смогут прочитать из кэша L1d.

Примеры барьеров:

           strong               weak
x86        mfence               none needed unless you're using NT stores
ARM        dmb sy               isb,  dmb st, dmb ish, etc.
POWER      hwsync               lwsync, isync, ...

ARM имеет «внутренние» и «внешние общие совместно используемые домены».».Я действительно не знаю, что это значит, мне не приходилось с этим сталкиваться, но эта страница документирует различные доступные формы барьера памяти данных.dmb st ожидает только завершения предыдущих хранилищ, поэтому я думаю, что это всего лишь барьер StoreStore, и поэтому он слишком слаб для хранилища релизов C ++ 11, которому также необходимо упорядочить более ранние загрузки для переупорядочения LoadStore.См. Также C / C ++ 11 отображений для процессоров : обратите внимание, что seq-cst может быть достигнут с полными барьерами вокруг каждого магазина или с барьерами перед загрузкой, а также перед магазинами.Делать грузы дешевле, как правило, лучше всего.

ARM ISB очищает кэши инструкций.(У ARM нет связного i-кеша, поэтому после записи кода в память вам необходим ISB, прежде чем вы сможете надежно перейти туда и выполнить эти байты в качестве инструкций.)

В POWER имеется большой выбор доступных барьеров, в том числе легкий (неполный барьер) и тяжелый синхронизатор (полный барьер), упомянутые в статье Джеффа Прешинга, ссылка на которую приведена выше.


Однонаправленный барьер - это то, что вы получаете отрелиз-магазин или приобретение-загрузка .Хранилище релизов в конце критической секции (например, чтобы разблокировать спин-блокировку) должно обеспечить, чтобы загрузки / хранилища внутри критической секции не появлялись позже, но не должна задерживаться позжезагружается до тех пор, пока lock=0 не станет глобально видимым.

У Джеффа Прешинга тоже есть статья на эту тему: Семантика получения и выпуска


"полный"Терминология «частичного» барьера обычно не используется для ограничения одностороннего переупорядочения в хранилище релизов или в приобретении.Фактический релиз fence (в C ++ 11, std::atomic_thread_fence(std::memory_order_release)) блокирует переупорядочение хранилищ в обоих направлениях, в отличие от хранилища релизов для определенного объекта.

Это тонкое различие вызывало путаницу в прошлом (даже среди экспертов!).У Джеффа Прешинга есть еще одна отличная статья, объясняющая это: Заборы для получения и выпуска не работают так, как вы ожидаете .

Вы правы, что односторонний барьер, который не былпривязать к магазину или грузу было бы не очень полезно;вот почему такой вещи не существует.: P Это может изменить порядок неограниченного расстояния в одном направлении и оставить все операции для изменения порядка друг с другом.


Что именно делает atomic_thread_fence(memory_order_release)?

C11 ( n1570 Раздел 7.17.4 Ограждения ) определяет его только с точки зрения создания отношения синхронизация-с забором-загрузкой или приобретением-забором, когда разделительный-забор используется перед атомным хранилищем (расслаблено)или иным образом) к тому же объекту нагрузка получает доступ.(C ++ 11 имеет в основном то же определение, но обсуждение с @EOF в комментариях привело к версии C11.)

Это определение с точки зрения чистого эффекта, а не механизма его достижения, не напрямую скажите нам, что он делает или не позволяет.Например, подраздел 3 гласит:

3) Ограничение освобождения A синхронизируется с атомарной операцией B, которая выполняет операцию получения на атомарном объекте M, если существует атомарная операция X, такая, что A секвенируется передX, X изменяет M, а B читает значение, записанное X, или значение, записанное любым побочным эффектом в гипотетической последовательности деблокирования, X будет начинаться, если бы это была операция деблокирования

Так в потоке записи, это говорит о коде, как это:

stuff           // including any non-atomic loads/stores

atomic_thread_fence(mo_release)  // A
M=X                              // X
  // threads that see load(M, acquire) == X also see stuff

Синхронизация означает, что потоки, которые видят значение из M=X (прямо или косвенно через последовательность выпуска), также видят все stuff и читают неатомарные переменные без Data Race UB.

Это позволяет нам сказать что-то о том, что разрешено / не разрешено:

Это двусторонний барьер для атомных магазинов .Они не могут пересекать его в любом направлении, поэтому расположение барьера в порядке памяти этого потока ограничено атомными хранилищами до и после.Любое более раннее хранилище может быть частью stuff для некоторых M, любое более позднее хранилище может быть M, с которым синхронизируется начальная загрузка (или загрузка + приобретение-забор).

Это односторонний барьер для атомных нагрузок: более ранние должны оставаться перед барьером, но более поздние могут двигаться выше барьера.M=X может быть только магазином (или частью магазина RMW).

Это односторонний барьер для неатомных нагрузок / хранилищ : неатомарные хранилища могут бытьчасть stuff, но не может быть X, потому что они не атомарные.Можно допустить, чтобы последующие загрузки / сохранения в этой теме отображались другим потокам до M=X.(Если неатомарная переменная изменена до и после барьера, то ничто не сможет безопасно прочитать ее даже после синхронизации с этим барьером, если только у читателя не будет способа остановить продолжение потока и создать Data Race UBТаким образом, компилятор может и должен переупорядочить foo=1; fence(release); foo=2; в foo=2; fence(release);, устраняя мертвое хранилище foo=1. Но понижение foo=1 после барьера законно только по технической причине, что ничто не может отличить без UB.)

В качестве детали реализации, ограничитель релиза C11 может быть сильнее этого (например, двухсторонний барьер для большего количества видов переупорядочения во время компиляции), но не слабее.На некоторых архитектурах (например, ARM) единственной достаточно сильной опцией может быть инструкция asm с полным барьером.А что касается ограничений на переупорядочение во время компиляции, компилятор может не разрешить эти односторонние переупорядочения только для упрощения реализации.

В основном, эта комбинированная двухсторонняя / односторонняя природа имеет значение только для переупорядочение во время компиляции .Процессоры не делают различий между атомарными и неатомарными хранилищами.Неатомарная - это всегда та же команда asm, что и расслабленная атомарная (для объектов, которые помещаются в один регистр).

Инструкции барьера ЦП, заставляющие ядро ​​ждать, пока более ранние операции не станут видны глобально, обычно являются двусторонними барьерами;они определены в терминах операций, которые становятся глобально видимыми в согласованном представлении памяти, совместно используемой всеми ядрами, а не в стиле C / C ++ 11 для создания отношений синхронизация с.(Помните, что операции могут потенциально стать видимыми для некоторых других потоков, прежде чем они станут глобально видимыми для всех потоков: Будут ли всегда видны две атомарные записи в разные места в разных потокахтот же порядок в других потоках? . Но при наличии только барьеров, препятствующих переупорядочению в физическом ядре, последовательная согласованность может быть восстановлена.)

Для ограждения релиза C ++ 11 нужны барьеры LoadStore + StoreStore, ноне LoadLoad.Процессор, который позволяет вам получить только эти 2, но не все 3 «дешевых» барьера, позволил бы переупорядочивать нагрузки в одном направлении через команду барьера, в то же время блокируя хранилища в обоих направлениях.

Фактически слабо упорядоченный SPARCкак это, и использует LoadStore и т. д. терминологию (вот где Джефф Прешинг взял терминологию для своих статей).http://blog.forecode.com/2010/01/29/barriers-to-understanding-memory-barriers/ показывает, как они используются.(В более поздних версиях SPARC используется модель памяти TSO (Total Store Order). Я думаю, что это похоже на x86, где аппаратное обеспечение создает иллюзию операций с памятью, происходящих в порядке программ, за исключением переупорядочения StoreLoad.)

...