Бамбергский барьер
- 1 year ago
- 0
- 0
Барьер памяти ( англ. memory barrier, membar, memory fence, fence instruction) — вид барьерной инструкции , которая приказывает компилятору (при генерации инструкций) и центральному процессору (при исполнении инструкций) устанавливать строгую последовательность между обращениями к памяти до и после барьера. Это означает, что все обращения к памяти перед барьером будут гарантированно выполнены до первого обращения к памяти после барьера.
Барьеры памяти необходимы, так как большинство современных процессоров использует оптимизации производительности, которые могут привести к
переупорядочиванию инструкций
. Также переупорядочивание обращений к памяти может быть вызвано компилятором в процессе оптимизации использования регистров целевого процессора. Такие перестановки обычно не влияют на корректность программы с одним потоком исполнения, но могут вызвать непредсказуемое поведение в многопоточных программах. Правила изменения порядка исполнения инструкций зависят от архитектуры. Некоторые архитектуры предоставляют несколько типов барьеров с различными гарантиями. Например,
amd64
предоставляет следующие инструкции:
SFENCE
(
англ.
store fence),
LFENCE
(
англ.
load fence),
MFENCE
(
англ.
memory fence)
.
Intel Itanium
обеспечивает отдельные «запоминающие» (
англ.
acquire) и «освобождающие» (
англ.
release) барьеры памяти, которые учитывают видимость операций чтения после записи с точки зрения читателя и писателя соответственно.
Барьеры памяти, как правило, используются при реализации примитивов синхронизации , неблокирующих структур данных и драйверов, которые взаимодействуют с аппаратным обеспечением .
Следующая программа исполняется на двух процессорах.
Изначально ячейки памяти
x
и
f
содержат значение
0
. Программа в процессоре #1 находится в цикле, пока
f
равен нулю, затем она печатает значение
x
. Программа в процессоре #2 записывает значение
42
в
x
, а затем сохраняет значение
1
в
f
. Псевдокод для двух программных фрагментов:
Процессор #1:
while (f == 0) { } // Здесь необходим барьер print x;
Процессор #2:
x = 42; // Здесь необходим барьер f = 1;
Хотя ожидается, что
print
всегда напечатает «42», но если процессор #2 изменит порядок исполнения инструкций и вначале изменит значение
f
, то print может вывести «0». Аналогично, процессор #1 может прочитать
x
перед
f
, и print снова выведет не ожидаемое значение. Для большинства программ ни одна из этих ситуаций не приемлема. Барьер памяти для процессора #2 может быть вставлен перед изменением значения
f
. Также можно вставить барьер для процессора #1 перед чтением
x
.
Барьеры памяти работают только на аппаратном уровне. Компиляторы могут также переупорядочить инструкции как часть оптимизации программы. Меры по предотвращению переупорядочивания необходимы только для данных, которые не защищены примитивами синхронизации.
В языках С и C++ ключевое слово volatile предназначено для исключения оптимизаций компилятора. Используется чаще всего для работы с отображаемым в память вводом-выводом. Однако данное ключевое слово (в отличие от Java) никак не обеспечивает атомарности и защиты от внеочередного исполнения.