Инструкция по переупорядочению на Intel
Я пытаюсь понять порядок перестановки команд на следующем простом примере:
int a;
int b;
void foo(){
a = 1;
b = 1;
}
void bar(){
while(b == 0) continue;
assert(a == 1);
}
Известно, что в этом примере утверждение может потерпеть неудачу, если выполняется один поток foo
и другой выполняет bar
, Но я не понимаю почему. Я обратился к руководству Intel Vol. 3А, 8.2.2 и обнаружил следующее:
Записи в память не переупорядочиваются с другими записями, за следующими исключениями:
- потоковые хранилища (записи), выполняемые с использованием невременных команд перемещения (MOVNTI, MOVNTQ, MOVNTDQ, MOVNTPS и MOVNTPD); а также
- строковые операции (см. Раздел 8.2.4.1).
Здесь нет строковых операций, так как я не заметил NT
переместить инструкции. Итак... Почему возможно изменение порядка записей?
Или память имеет значение в
Пишет в память не переупорядочено
? Итак, когда у нас есть a
а также b
кеширование и запись происходят не в основную память, а в кеш они могут быть.
2 ответа
Ваша предпосылка неверна. Только переупорядочение во время компиляции может сломать этот пример на x86 1.
x86 asm хранит релиз-магазины. Они могут фиксировать данные только из буфера хранилища в кэш L1d в программном порядке.
a
не может быть в общем состоянии после b=1
виден; это означало бы, что поток работает foo
пусть его магазины совершают не в порядке. Это то, что Записывает в память, не переупорядочивается с другими средствами записи, для хранения в кешируемой памяти.
Если он снова находится в общем состоянии после того, как был аннулирован RFO из запущенного потока foo
то будет иметь обновленное значение a
,
Сноска 1. Конечно, спин-петля оптимизируется в if (b==0) infinite_loop
потому что data-race UB позволяет компилятору поднять нагрузку. См. Программирование MCU - оптимизация C++ O2 прерывается во время цикла.
Вы, кажется, спрашиваете о правилах C, предполагая, что код будет наивно / прямо переведен в x86 asm. Вы могли бы получить это с расслабленной атомистикой, но не volatile
так как volatile
доступ не может быть переупорядочен (во время компиляции) с другими volatile
доступ.
Если один поток был запущен foo
а другой бежал bar
тогда поведение вашей программы будет неопределенным.
Вы не можете одновременно выполнять чтение и запись в неатомарную переменную, такую как int
,
Таким образом, запись инструкций допустима в этом случае.