#include <atomic>
std::atomic<int> val{1};
const auto my_order = std::memory_order_relaxed; // const lvalue
int main()
{
val.store(42, my_order);
}
Этот код не имеет отношения, но я заметил нечто странное в отношении упорядочения памяти. Компилятор создает следующую сборку для main (x86_64, g ++ 6.2.1, скомпилированную с -O3):
0x00000000004004c0 <+0>: movl $0x2a,0x200b5a(%rip) # 0x601024 <val>
0x00000000004004ca <+10>: xor %eax,%eax
0x00000000004004cc <+12>: retq
Не существует специальных инструкций процессора для атомарного режима, который ожидается на x86 с std::memory_order_relaxed
упорядоченность.
Тем не менее, когда const
квалификатор удален из my_order
auto my_order = std::memory_order_relaxed; // non-const lvalue
сгенерированная компилятором сборка становится:
0x00000000004004c0 <+0>: movl $0x2a,0x200b5a(%rip) # 0x601024 <val>
0x00000000004004ca <+10>: xor %eax,%eax
0x00000000004004cc <+12>: mfence
0x00000000004004cf <+15>: retq
mfence
Инструкция, кажется, указывает на то, что std::memory_order_seq_cst
теперь используется порядок (по умолчанию). Это немного удивительно для меня. Даже если my_order
является lvalue (нетрадиционно для указания порядка памяти), оно передается по значению (все еще std::memory_order_relaxed
) и я не вижу, какconst
изменил бы результат. Я не мог найти конкретную перегрузку в заголовочных файлах библиотеки для этого.
С Clang я вижу аналогичные результаты, за исключением того, что он использует xchg
, который является лягушатным способом выражения последовательной последовательности.
Чем можно объяснить разницу?
Как правило, когда компилятор не может доказать, что параметр порядка известен во время компиляции, он не рискует и принимает наихудший случай.
В случае my_order
это неconst
глобальная переменная, компилятор не может знать, каким будет фактическое значение, когда store
выполняется и поэтому он собирается использовать std::memory_order_seq_cst
,
Если переменная объявлена const
, параметр порядка вступит в силу и mfence
инструкция ушла.
Других решений пока нет …