stringtranslate.com

Барьер памяти

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

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

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

Пример

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

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

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

Первоначально ячейки памяти xи fобе содержат значение 0. Программный поток, работающий на процессоре №1, зацикливается, пока значение fравно нулю, а затем печатает значение x. Программный поток, работающий на процессоре №2, сохраняет значение 42в файле x, а затем сохраняет его 1в файле f. Ниже приведен псевдокод двух фрагментов программы.

Шаги программы соответствуют отдельным инструкциям процессора.

В случае процессора PowerPC эта eioioинструкция в качестве ограничения памяти гарантирует, что любые операции загрузки или сохранения, ранее инициированные процессором, будут полностью завершены по отношению к основной памяти до того, как любые последующие операции загрузки или сохранения, инициированные процессором, получат доступ к основной памяти. Память. [1] [2]

Поток № 1. Ядро № 1:

 в то время как ( ж == 0 ); // Здесь требуется ограничение памяти print x ;      

Поток № 2. Ядро № 2:

 х = 42 ; // Здесь требуется ограничение памяти f = 1 ;      

Можно было бы ожидать, что оператор печати всегда будет печатать число «42»; однако, если операции сохранения потока № 2 выполняются вне очереди, возможно, что они fбудут обновлены раньше x , и поэтому оператор печати может напечатать «0». Аналогичным образом, операции загрузки потока № 1 могут выполняться вне очереди, и их можно xпрочитать до того, как f будет проверено, и снова оператор печати может поэтому напечатать неожиданное значение. Для большинства программ ни одна из этих ситуаций неприемлема. Перед назначением потока № 2 необходимо вставить барьер памяти, чтобы fгарантировать, что новое значение xбудет видно другим процессорам во время или до изменения значения f. Еще одним важным моментом является то, что перед доступом потока № 1 также должен быть установлен барьер памяти, чтобы xгарантировать, что значение xне будет прочитано до того, как будет видно изменение значения f.

Другой пример: водитель выполняет следующую последовательность действий:

 подготовить данные для аппаратного модуля // Здесь требуется ограничение памяти , чтобы аппаратный модуль обработал данные              

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

Еще один наглядный пример (нетривиальный, возникающий на практике) см. в разделе « двойная проверка блокировки» .

Многопоточное программирование и видимость памяти

Многопоточные программы обычно используют примитивы синхронизации, предоставляемые средой программирования высокого уровня, например Java или .NET , или интерфейсом прикладного программирования (API), например POSIX Threads или Windows API . Примитивы синхронизации, такие как мьютексы и семафоры , предназначены для синхронизации доступа к ресурсам из параллельных потоков выполнения. Эти примитивы обычно реализуются с барьерами памяти, необходимыми для обеспечения ожидаемой семантики видимости памяти . В таких средах явное использование барьеров памяти обычно не требуется.

Выполнение вне порядка и оптимизация переупорядочения компилятора

Инструкции барьера памяти устраняют эффекты переупорядочения только на аппаратном уровне. Компиляторы также могут изменять порядок инструкций в рамках процесса оптимизации программы . Хотя влияние на поведение параллельных программ в обоих случаях может быть одинаковым, в целом необходимо принимать отдельные меры для запрета оптимизации переупорядочения компилятором данных, которые могут совместно использоваться несколькими потоками выполнения.

В C и C++ ключевое слово Летучие предназначалось для того, чтобы позволить программам C и C++ напрямую обращаться к отображенному в памяти вводу-выводу. Ввод-вывод с отображением в памяти обычно требует, чтобы операции чтения и записи, указанные в исходном коде, происходили в точном указанном порядке, без пропусков. Пропуски или изменение порядка операций чтения и записи компилятором могут нарушить связь между программой и устройством, к которому осуществляется ввод-вывод с отображением в памяти. Компилятор AC или C++ не может пропускать операции чтения и записи в энергозависимые области памяти, а также не может переупорядочивать операции чтения/записи относительно других подобных действий для той же энергозависимой ячейки (переменной). Ключевое слово Летучий не гарантирует наличие барьера памяти для обеспечения согласованности кэша. Таким образом, одного только использования Volaty недостаточно для использования переменной для межпотоковой связи во всех системах и процессорах. [3]

Стандарты C и C++, предшествовавшие C11 и C++11, не затрагивают несколько потоков (или несколько процессоров) [4] , и поэтому полезность voluty зависит от компилятора и оборудования. Хотя энергозависимость гарантирует, что энергозависимое чтение и энергозависимая запись будут происходить в точном порядке, указанном в исходном коде, компилятор может генерировать код (или ЦП может изменить порядок выполнения), так что энергозависимое чтение или запись переупорядочиваются относительно независящих операций. - Летучий читает или записывает, что ограничивает его полезность в качестве межпотокового флага или мьютекса.

Смотрите также

Рекомендации

  1. ^ Мэй, Кэти; Силха, Эд; Симпсон, Эйк; Уоррен, Хэнк (1993). Архитектура PowerPC: спецификация нового семейства RISC-процессоров . Издательство Морган Кауфманн. п. 350. ИСБН 1-55860-316-6.
  2. ^ Качмарчик, Кэри (1995). Оптимизация кода PowerPC . Издательство Аддисон-Уэсли. п. 188. ИСБН 0-201-40839-2.
  3. ^ Корбет, Джонатан. «Почему не следует использовать класс типов «изменчивый»». Кернел.орг . Проверено 13 апреля 2023 г.
  4. ^ Бем, Ганс (июнь 2005 г.). Потоки не могут быть реализованы как библиотека. Материалы конференции ACM SIGPLAN 2005 года по проектированию и реализации языков программирования . Ассоциация вычислительной техники . п. 261. CiteSeerX 10.1.1.308.5939 . дои : 10.1145/1065010.1065042. ISBN  1595930566.

Внешние ссылки