Как работает барьер памяти?

В Windows есть три встроенные функции компилятора для реализации барьера памяти:

1. _ReadBarrier;

2. _WriteBarrier;

3. _ReadWriteBarrier;

Однако я обнаружил странную проблему: _ReadBarrier кажется фиктивной функцией, которая ничего не делает! Ниже приведен мой ассемблерный код, сгенерированный VC ++ 2012.

Мой вопрос: как реализовать функцию барьера памяти в инструкциях по сборке?

int main()
{
013EEE10  push        ebp
013EEE11  mov         ebp,esp
013EEE13  sub         esp,0CCh
013EEE19  push        ebx
013EEE1A  push        esi
013EEE1B  push        edi
013EEE1C  lea         edi,[ebp-0CCh]
013EEE22  mov         ecx,33h
013EEE27  mov         eax,0CCCCCCCCh
013EEE2C  rep stos    dword ptr es:[edi]
int n = 0;
013EEE2E  mov         dword ptr [n],0
n = n + 1;
013EEE35  mov         eax,dword ptr [n]
013EEE38  add         eax,1
013EEE3B  mov         dword ptr [n],eax
_ReadBarrier();
n = n + 1;
013EEE3E  mov         eax,dword ptr [n]
013EEE41  add         eax,1
013EEE44  mov         dword ptr [n],eax
}
013EEE56  xor         eax,eax
013EEE58  pop         edi
013EEE59  pop         esi
013EEE5A  pop         ebx
013EEE5B  add         esp,0CCh
013EEE61  cmp         ebp,esp
013EEE63  call        __RTC_CheckEsp (013EC3B0h)
013EEE68  mov         esp,ebp
013EEE6A  pop         ebp
013EEE6B  ret

4

Решение

_ReadBarrier, _WriteBarrier, а также _ReadWriteBarrier являются особенности, которые влияют на то, как компилятор может изменить порядок кода; они не имеют абсолютно никакого отношения к барьерам памяти ЦП и действительны только для определенных видов памяти (см. «Затрагиваемая память»). Вот).

MemoryBarrier() это свойство, которое вы используете для создания барьера памяти процессора. Тем не менее, рекомендация от Microsoft заключается в использовании std::atomic<T> идти вперед с VC ++.

8

Другие решения

Современные процессоры способны выполнять инструкции намного раньше, чем они фактически «завершают» инструкции, поэтому используются барьеры памяти, чтобы помешать их выполнению далеко вперед, когда дело доходит до определенных типов операций с памятью, где строгое упорядочение требуется — в большинстве случаев не имеет значения, пишете ли вы в переменную a перед переменной b или b перед переменной a. Но иногда это так.

Набор команд x86 имеет lfence, sfence а также fence, которые являются инструкциями, которые «забивают» загружает, хранит и все операции с памятью соответственно. Дело в том, что инструкция «забор» или «барьер» заключается в обеспечивать что все инструкции, предшествующие инструкции барьера, завершили свою загрузку, сохраняют или обе перед следующей инструкцией после продолжения барьера.

Это важно, если вы реализуете, например, семафоры, мьютексы или аналогичные инструкции, так как важно сохранить значение, говорящее «Я заблокировал семафор», прежде чем вы, например, продолжите читать другие данные. В противном случае все может пойти не так, скажем так.

Обратите внимание, что если вы ДЕЙСТВИТЕЛЬНО не знаете, что вы делаете с барьерами памяти, вероятно, лучше НЕ использовать их — и полагаться на уже существующий код, который решает ту же проблему — std::atomic одно место для финансирования такого кода. Я написал довольно сложный код, но только один или два раза мне понадобился барьер памяти в моем коде.

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

4

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

Второе — это то, что требуется для оборудования, даже в
многопоточный контекст, меняется. На большинстве машин я
работал на (более сорока лет), машина никогда не требовалась
что-нибудь; барьеры становятся актуальными, только если машина имеет
сложные чтения и записи конвейеров. (Самые ранние машины
не было даже инструкций по забору или барьеру, поэтому сгенерированный
код должен быть пустым.)

0
По вопросам рекламы [email protected]