Я отвечу за то, что происходит в реальных реализациях на реальных процессорах, потому что ответ, основанный только на стандарте, едва ли может сказать что-нибудь полезное о времени или "непосредственности".
MESI - это просто деталь реализации, которая ISO C ++ не о чем говорить. Гарантии, предоставляемые ISO C ++, касаются только порядка, а не фактического времени . ISO C ++ специально не указывается c, чтобы не предполагать, что он будет выполняться на "нормальном" процессоре. Реализация на некогерентной машине, которая требовала явных сбросов для видимости хранилища, теоретически возможна (хотя, вероятно, ужасна для выполнения операций release / acquis и seq-cst)
C ++ недостаточно определен c достаточно о сроках, чтобы даже разрешить реализацию в одноядерной кооперативной многозадачной системе (без упреждения), при этом компилятор иногда вставляет произвольные выходы. (Бесконечные циклы без какого-либо изменчивого доступа или ввода-вывода - UB). C ++ в системе, где на самом деле может выполняться одновременно только один поток, вполне нормально и возможно, при условии, что вы считаете временной интервал планировщика все еще «разумным» количеством времени. (Или меньше, если вы уступите или иным образом заблокируете.)
Даже модель формализма, используемая в ISO C ++ для предоставления гарантий относительно порядка, очень отличается от того, как аппаратные ISA определяют свои модели памяти. Формальные гарантии C ++ сугубо с точки зрения «происходит раньше» и синхронизируются с, а не «повторно» -заказывающими лакмусовыми тестами или чем-то подобным. Например, Как достичь барьера StoreLoad в C ++ 11? невозможно ответить за чистый формализм ISO C ++. И это показывает, насколько слабы гарантии C ++; например, существование полного порядка всех операций seq_cst не достаточно, чтобы подразумевать, что это происходит до того, как оно основано на этом, согласно формализму C ++. Но в реальной жизни этого достаточно для систем с когерентным кэшем и переупорядочением памяти только локальной (в пределах каждого ядра ЦП).
, когда поток A сохраняет значение в std::atomic
Это зависит от того, что вы подразумеваете под «созданием» хранилища.
Если вы имеете в виду фиксацию из буфера хранилища в кэш L1d, то да, это тот момент, когда хранилище становится на глобальном уровне, на обычной машине, которая использует MESI, чтобы дать всем ядрам процессора согласованное представление о памяти.
Хотя обратите внимание, что на некоторых ISA некоторые другие потоки могут видеть сохраняет до того, как они станут глобально видимыми через кеш . (т. е. аппаратная модель памяти не может быть «multi-copy atomi c» и допускает переупорядочение IRIW. POWER - единственный известный мне пример, который делает это в реальной жизни. См. Будет два атома c пишет в разные места в разных потоках всегда отображаются в одном и том же порядке другими потоками? для получения подробной информации о механизме HW: Пересылка хранилища для устаревших, также называемых градуированных хранилищ, между потоками SMT.)
Если вы имеете в виду выполнение локально, чтобы более поздние загрузки в этом потоке могли его видеть, то нет. std :: atomi c может использовать параметр memory_order более слабый, чем seq_cst.
Все У основных ISA есть правила упорядочения памяти, достаточно слабые, чтобы позволить буферу хранилища отделить выполнение команды от фиксации до кэша. Это также позволяет спекулятивно выполнять заказы вне очереди, предоставляя магазинам где-то приватное жилье после выполнения, прежде чем мы убедимся, что они были на правильном пути выполнения. (Хранилища не могут зафиксировать L1d до тех пор, пока инструкция хранилища не выйдет из вышедшей из строя части серверной части, и, следовательно, известно, что она не является спекулятивной.)
Если вы хотите дождаться чтобы ваш магазин был виден другим потокам перед выполнением последующих загрузок, используйте atomic_thread_fence(memory_order_seq_cst);
. (Который на "нормальных" ISA со стандартным выбором C ++ -> сопоставлений asm будет компилировать полный барьер).
На большинстве ISA хранилище seq_cst (по умолчанию) также будет останавливать все последующие загрузки (и хранилища) в этом потоке, пока хранилище не станет глобально видимым. Но на AArch64 STLR является хранилищем с последовательным выпуском, и выполнение более поздних загрузок / хранилищ не должно останавливаться, пока / пока не будет выполнено выполнение LDAR (получение загрузки), пока STLR все еще находится в буфере хранения. Это реализует семантику S C настолько слабо, насколько это возможно, предполагая, что аппаратное обеспечение AArch64 действительно работает таким образом, а не просто рассматривает его как хранилище + полный барьер.
Но обратите внимание, что необходима только блокировка более поздних загрузок / хранилищ; вышедший из строя exe c инструкций ALU по регистрам все еще может продолжаться. Но если вы ожидаете какой-то эффект синхронизации из-за цепочек зависимостей операций FP, например, это не то, от чего вы можете зависеть в C ++.
Даже если вы используете seq_cst, ничего не происходит в эта ветка перед магазином видна другим, это еще не мгновенно. Задержка между ядрами на реальном оборудовании может составлять порядка , может быть, 40 нс на современных Intel x86, например. (Этот поток не должен так долго останавливаться на инструкции барьера памяти; иногда это происходит из-за отсутствия кэша в другом потоке, пытающемся прочитать строку, которая была аннулирована RFO этого ядра, чтобы получить исключительное право собственности.) Или, конечно, намного дешевле для логических ядер, которые совместно используют кэш L1d физического ядра: Каковы затраты на задержку и пропускную способность совместного использования производителем и потребителем расположения памяти между гипер-братьями и сестрами и не-гипер-братьями?