Барьер памяти
Эта статья нуждается в дополнительных цитатах для проверки . ( январь 2016 г. ) |
В вычислительной технике барьер памяти , также известный как мембар , ограждение памяти или инструкция ограждения , представляет собой тип барьера инструкции , которая заставляет центральный процессор (ЦП) или компилятор применять ограничение порядка для операций с памятью , выполняемых до и после барьера. инструкция. Обычно это означает, что операции, выполненные до барьера, гарантированно будут выполнены до операций, выполненных после барьера.
Барьеры памяти необходимы, поскольку большинство современных процессоров используют оптимизацию производительности, которая может привести к нарушению порядка выполнения . Такое изменение порядка операций с памятью (загрузка и сохранение) обычно остается незамеченным в пределах одного потока выполнения , но может вызвать непредсказуемое поведение в параллельных программах и драйверах устройств , если его тщательно не контролировать. архитектуры Точная природа ограничения порядка зависит от оборудования и определяется моделью упорядочения памяти . Некоторые архитектуры предусматривают несколько барьеров для реализации различных ограничений порядка.
Барьеры памяти обычно используются при реализации низкоуровневого машинного кода , который работает с памятью, совместно используемой несколькими устройствами. Такой код включает в себя синхронизации примитивы и структуры данных без блокировки в многопроцессорных системах, а также драйверы устройств, которые взаимодействуют с компьютерным оборудованием .
Пример
[ редактировать ]Когда программа запускается на однопроцессорной машине, аппаратное обеспечение выполняет необходимый учет, чтобы гарантировать, что программа выполняется так, как если бы все операции с памятью выполнялись в порядке, указанном программистом (порядок программы), поэтому барьеры памяти не нужны. Однако, когда память используется несколькими устройствами, например, другими процессорами в многопроцессорной системе или периферийными устройствами, отображаемыми в памяти , неупорядоченный доступ может повлиять на поведение программы. Например, второй ЦП может видеть изменения памяти, выполненные первым ЦП, в последовательности, отличной от порядка программы.
Программа запускается через процесс, который может быть многопоточным (т.е. программный поток, такой как pthreads, в отличие от аппаратного потока). Различные процессы не используют совместное пространство памяти, поэтому это обсуждение не применимо к двум программам, каждая из которых выполняется в разных процессах (следовательно, в разных областях памяти). Это применимо к двум или более (программным) потокам, выполняющимся в одном процессе (т. е. в одном пространстве памяти, где несколько программных потоков совместно используют одно пространство памяти). Несколько программных потоков в рамках одного процесса могут выполняться одновременно на многоядерном процессоре .
Следующая многопоточная программа, работающая на многоядерном процессоре, дает пример того, как такое внеочередное выполнение может повлиять на поведение программы:
Изначально места памяти x
и f
оба имеют значение 0
. Программный поток, работающий на процессоре №1, зацикливается, пока значение f
равно нулю, то он печатает значение x
. Программный поток, работающий на процессоре №2, сохраняет значение 42
в x
а затем сохраняет значение 1
в f
. Ниже приведен псевдокод двух фрагментов программы.
Шаги программы соответствуют отдельным инструкциям процессора.
В случае процессора PowerPC eioio
В качестве ограничения памяти инструкция гарантирует, что любые операции загрузки или сохранения, ранее инициированные процессором, будут полностью завершены по отношению к основной памяти до того, как любые последующие операции загрузки или сохранения, инициированные процессором, получат доступ к основной памяти. [1] [2]
Поток № 1. Ядро № 1:
while (f == 0);
// Memory fence required here
print x;
Поток № 2. Ядро № 2:
x = 42;
// Memory fence required here
f = 1;
Можно было бы ожидать, что оператор печати всегда будет печатать число «42»; однако, если операции сохранения потока № 2 выполняются вне очереди, это возможно для f
будет обновлено раньше x
, и поэтому оператор печати может напечатать «0». Аналогичным образом, операции загрузки потока № 1 могут выполняться вне очереди, и это возможно для x
быть прочитанным раньше f
отмечен, и оператор печати снова может напечатать неожиданное значение. Для большинства программ ни одна из этих ситуаций неприемлема. Барьер памяти должен быть установлен перед назначением потока № 2 f
чтобы гарантировать, что новое значение x
виден другим процессорам во время или до изменения значения f
. Еще один важный момент: барьер памяти также должен быть установлен перед доступом потока № 1 к x
чтобы гарантировать ценность x
не читается до того, как будет видно изменение значения f
.
Другой пример: водитель выполняет следующую последовательность действий:
prepare data for a hardware module
// Memory fence required here
trigger the hardware module to process the data
Если операции сохранения процессора выполняются вне очереди, аппаратный модуль может сработать до того, как данные будут готовы в памяти.
Еще один наглядный пример (нетривиальный, возникающий на практике) см. в разделе « двойная проверка блокировки» .
Многопоточное программирование и видимость памяти
[ редактировать ]Многопоточные программы обычно используют примитивы синхронизации, предоставляемые средой программирования высокого уровня, например Java или .NET , или интерфейсом прикладного программирования (API), например POSIX Threads или Windows API . Примитивы синхронизации, такие как мьютексы и семафоры, предназначены для синхронизации доступа к ресурсам из параллельных потоков выполнения. Эти примитивы обычно реализуются с барьерами памяти, необходимыми для обеспечения ожидаемой семантики видимости памяти . В таких средах явное использование барьеров памяти обычно не требуется.
Выполнение вне порядка и оптимизация переупорядочения компилятора
[ редактировать ]Инструкции барьера памяти устраняют эффекты переупорядочения только на аппаратном уровне. Компиляторы также могут изменять порядок инструкций в рамках процесса оптимизации программы . Хотя влияние на поведение параллельных программ в обоих случаях может быть одинаковым, в целом необходимо принимать отдельные меры для запрета оптимизации переупорядочения компилятором данных, которые могут совместно использоваться несколькими потоками выполнения.
В C и C ++ Ключевое слово volutity было предназначено для того, чтобы позволить программам C и C++ напрямую обращаться к отображенному в памяти вводу-выводу. Ввод-вывод с отображением в памяти обычно требует, чтобы операции чтения и записи, указанные в исходном коде, происходили в точном указанном порядке, без пропусков. Пропуски или изменение порядка операций чтения и записи компилятором могут нарушить связь между программой и устройством, к которому осуществляется ввод-вывод с отображением в памяти. Компилятор AC или C++ не может пропускать операции чтения и записи в энергозависимые области памяти, а также не может переупорядочивать операции чтения/записи относительно других подобных действий для той же энергозависимой ячейки (переменной). Ключевое слово Летучий не гарантирует наличие барьера памяти для обеспечения согласованности кэша. Поэтому использование Одной только Летучести недостаточно для использования переменной для межпотоковой связи во всех системах и процессорах. [3]
Стандарты C и C++, предшествующие C11 и C++11, не охватывают несколько потоков (или несколько процессоров). [4] и, как следствие, полезность Летучий зависит от компилятора и оборудования. Хотя Летучий гарантирует, что энергозависимые чтения и энергозависимые записи будут происходить в точном порядке, указанном в исходном коде, компилятор может генерировать код (или ЦП может изменить порядок выполнения), так что изменчивый порядок чтения или записи переупорядочивается относительно не-зависимых операций. Летучий читает или записывает, что ограничивает его полезность в качестве межпотокового флага или мьютекса.
См. также
[ редактировать ]Ссылки
[ редактировать ]- ^ Мэй, Кэти; Силха, Эд; Симпсон, Эйк; Уоррен, Хэнк (1993). Архитектура PowerPC: спецификация нового семейства RISC-процессоров . Издательство Морган Кауфманн. п. 350. ИСБН 1-55860-316-6 .
- ^ Качмарчик, Кэри (1995). Оптимизация кода PowerPC . Издательство Аддисон-Уэсли. п. 188. ИСБН 0-201-40839-2 .
- ^ Корбет, Джонатан. «Почему не следует использовать класс типа «изменчивый»» . Кернел.орг . Проверено 13 апреля 2023 г.
- ^ Бём, Ганс (июнь 2005 г.). Потоки не могут быть реализованы как библиотека . Материалы конференции ACM SIGPLAN 2005 года по проектированию и реализации языков программирования . Ассоциация вычислительной техники . п. 261. CiteSeerX 10.1.1.308.5939 . дои : 10.1145/1065010.1065042 . ISBN 1595930566 .
Внешние ссылки
[ редактировать ]- Барьеры памяти: аппаратный взгляд для программных хакеров
- Проблемы с барьером памяти ядра Linux на нескольких типах процессоров
- Документация по барьерам памяти в ядре Linux
- Обработка порядка памяти в многопоточных приложениях с помощью Oracle Solaris Studio 12, обновление 2: часть 1, барьеры компилятора
- Обработка порядка памяти в многопоточных приложениях с помощью Oracle Solaris Studio 12, обновление 2: часть 2, барьеры памяти и ограничения памяти
- RCU в пользовательском пространстве: зверинец с барьером памяти