Модель памяти C ++ не дает вам свободного прохода для записи одного и того же значения.
Если два потока пишут в неатомарный объект без синхронизации, это просто условие гонки.А состояние гонки означает, что ваша программа выполняет неопределенное поведение.А неопределенное поведение, возникающее в любом месте выполнения вашей программы, означает, что поведение вашей программы, как до, так и после точки неопределенного поведения, никак не ограничено стандартом C ++.
Данный компилятор может свободнопредоставить более свободную модель памяти.Я ничего не знаю о том, что делают.
Одна вещь, которую вы должны понять, это то, что C ++ не является языком макросов ассемблера.Это не должно производить наивного ассемблера, которого вы себе представляете.Вместо этого C ++ пытается упростить для вашего компилятора создание ассемблера, а это совсем другое.
Компиляторы могут и действительно определить, «если X случится, мы получим неопределенное поведение; поэтому я буду оптимизировать вокруг факта»что X не происходит "при генерации кода.В этом случае здесь, компилятор может доказать , что программа с определенным поведением может когда-либо иметь один и тот же val
в двух разных несинхронизированных потоках.
Все это может произойти задолго до того, как какая-либо сборка будетГенерируемый.
И на уровне сборки, некоторые аппаратные средства могут делать забавные вещи с невыровненным назначением многобайтовых значений.Некоторое оборудование может (теоретически; на практике я ничего не знаю) вызывать прерывания, когда инструкции, которые утверждают, что однопотоковые записи выполняются в двух разных ядрах в одних и тех же байтах.
Так что этоUB в C ++.И когда у вас есть UB, вы должны проверять ассемблерный код, созданный вашей программой, везде, где может видеть компилятор, который касается этого.Если вы используете LTO, это означает, что во всей вашей программе, по крайней мере, везде, где вызывается или взаимодействует с вашим кодом, который выполняет UB, на неясное расстояние.
Просто напишите определенное поведение.И только если это окажется узким местом для критически важной производительности, вы должны тратить больше усилий на его оптимизацию (сначала более быстрое определенное поведение, и только в случае неудачи вы даже рассматриваете UB).