В первых двух статьях цикла мы рассмотрели четыре способа упорядочить доступ к памяти: load-acquire и store-release операции в первой части, барьеры чтения и записи в память — во второй. Теперь пришла очередь познакомиться с полными барьерами памяти, их влиянием на производительность, и примерами использования полных барьеров в ядре Linux.
Рассмотренные ранее примитивы ограничивают возможный порядок исполнения операций с памятью четырьмя различными способами:
- Load-acquire операции выполняются перед последующими чтениями и записями.
- Store-release операции выполняются после предыдущих чтений и записей.
- Барьеры чтения разделяют предыдущие и последующие чтения из памяти.
- Барьеры записи разделяют предыдущие и последующие записи в память.
Внимательный читатель заметил, что одна из возможных комбинаций в этом списке отсутствует:
Оказывается, обеспечить глобальный порядок записей и последующих чтений из памяти гораздо сложнее. Процессоры вынуждены прилагать отдельные усилия для этого. Сохранение такого порядка стоит недёшево и требует явных инструкций. Чтобы понять причину этих особенностей, нам придётся спуститься на уровнь ниже и присмотреться к тому, как процессоры работают с памятью.