Модель памяти с ++: синхронизируются ли загрузки seq_cst с хранилищами seq_cst?

В модели памяти C ++ существует общий порядок всех загрузок и сохранений всех последовательно согласованных операций. Мне интересно, как это взаимодействует с операциями, которые имеют другой порядок памяти, который упорядочен до / после последовательной последовательной загрузки.

Например, рассмотрим два потока:

std::atomic<int> a(0);
std::atomic<int> b(0);
std::atomic<int> c(0);

//////////////
// Thread T1
//////////////

// Signal that we've started running.
a.store(1, std::memory_order_relaxed);

// If T2's store to b occurs before our load below in the total
// order on sequentially consistent operations, set flag c.
if (b.load(std::memory_order_seq_cst) == 1) {
  c.store(1, std::memory_order_relaxed)
}


//////////////
// Thread T2
//////////////

// Blindly write to b.
b.store(1, std::memory_order_seq_cst)

// Has T1 set c? If so, then we know our store to b occurred before T1's load
// in the total order on sequentially consistent operations.
if (c.load(1, std::memory_order_relaxed)) {
  // But is this guaranteed to be visible yet?
  assert(a.load(1, std::memory_order_relaxed) == 1);
}

Гарантируется ли, что утверждение в T2 не может сработать?

Я ищу здесь подробные цитаты стандарта. В частности, я думаю, что для этого потребуется показать, что загрузка из b в T1 синхронизируется с хранилищем в b в T2, чтобы установить, что хранилище в a между потоками происходит до загрузки из a, но насколько я могу судить, в стандарте сказано, что memory_order_seq_cst хранилищ синхронизируются с нагрузками, но не наоборот.


person jacobsa    schedule 27.11.2017    source источник


Ответы (1)


Синхронизируются ли загрузки seq_cst с хранилищами seq_cst?

Они делают, если соблюдаются все необходимые требования; в вашем примере кода assert может стрелять

§29.3.3
Должен быть единый общий порядок S для всех операций memory_order_seq_cst

Этот общий порядок применяется к самим seq_cst операциям. Изолированно store(seq_cst) имеет семантику выпуска, тогда как load(seq_cst) имеет семантику получения.

§29.3.1-2 [atomics.order]
memory_order_release, memory_order_acq_rel и memory_order_seq_cst:
операция сохранения выполняет операцию освобождения в затронутой области памяти.
.....
§29.3 .1-4 [atomics.order]
memory_order_acquire, memory_order_acq_rel и memory_order_seq_cst:
операция загрузки выполняет операцию получения в затронутой области памяти.

Следовательно, атомарные операции с не seq_cst упорядочением (или неатомарными операциями) упорядочиваются относительно seq_cst операций в соответствии с правилами упорядочивания получения / выпуска:

  • store(seq_cst) операция не может быть переупорядочена с помощью какой-либо операции с памятью, которая идет до нее (т. е. выполняется раньше в программном порядке).
  • load(seq_cst) операция не может быть переупорядочена ни одной операцией с памятью, которая идет после нее.

В вашем примере, хотя c.store(relaxed) в T1 упорядочен (между потоками) после b.load(seq_cst) (load - это операция получения), c.load(relaxed) в T2 не упорядочен по отношению к b.store(seq_cst) (что является операцией выпуска, но не препятствует переупорядочению) .

Вы также можете посмотреть операции на a. Поскольку они ни к чему не упорядочены, a.load(relaxed) может вернуть 0, вызывая срабатывание assert.

person LWimsey    schedule 28.11.2017
comment
Спасибо, я согласен, и это было моим подозрением. Отчасти причина, по которой я спросил, заключается в том, что cppreference подразумевает более сильные гарантии: Любая операция с этим порядком памяти является как операцией получения, так и операцией выпуска; т.е. даже загрузка будет операцией выпуска. Кажется, это просто неверно? - person jacobsa; 28.11.2017
comment
и операция получения, и операция выпуска .. cppreference иногда может сбивать с толку; это применимо только к операциям чтения-изменения-записи. - person LWimsey; 28.11.2017
comment
Хорошо, согласен. Любая операция здесь по стандарту слишком сильна. - person jacobsa; 28.11.2017
comment
(нагрузка устанавливает барьер получения): это потенциально сбивающая с толку терминология, потому что atomic_thread_fence(mo_acquire) - это не просто операция получения, это двусторонний барьер, в отличие от одностороннего барьера получения load . Так что да, есть барьер, но мне не нравится ваше описание установки барьера приобретения, потому что это звучит как нагрузка + забор. - person Peter Cordes; 29.11.2017
comment
@PeterCordes Автономные заборы не задействованы, я старался избегать любых ссылок (и ни в одном вопросе они не упоминаются). Замечание о X86 не актуально в контексте вопроса о гарантиях, предоставляемых стандартом, а не конкретной реализации. - person LWimsey; 30.11.2017
comment
Я знаю, что не существует автономных ограждений, но фраза «приобретай барьер» заставила меня задуматься на минуту, имел ли ты это в виду, а затем побеспокоиться, что некоторые другие читатели подумают об этом. - person Peter Cordes; 30.11.2017
comment
@PeterCordes Ваш комментарий заставил меня (снова) понять, как легко запутать вещи, используя определенную терминологию .. Я обновил anser в попытке использовать 'standardese'. - person LWimsey; 30.11.2017
comment
Выглядит неплохо. Я не думаю, что в этом случае что-то потеряно, если говорить на стандартном языке, но другой подход мог бы заключаться в том, чтобы сказать, что приобретаемая нагрузка устанавливает односторонний барьер. Затем, возможно, скажите «В отличие от двухсторонних std::atomic ограждений» и свяжите статью Джеффа Прешинга о превосходных барьерах и грузах / магазинах. - person Peter Cordes; 30.11.2017
comment
@PeterCordes Для юриста, придерживающегося догматического языка (а я им не являюсь), проблема может заключаться в том, что стандарт не определяет операцию приобретения, которая действует как односторонний барьер. Все, что он говорит, это то, что операция выпуска синхронизируется с операцией получения и определяет гарантии, которые являются результатом этого процесса синхронизации. Конечно, единственная разумная реализация - использовать барьеры (и об этом также легче рассуждать), но AFAIK формально не требуется. - person LWimsey; 30.11.2017
comment
@PeterCordes Нет, я не имел в виду инструкции asm - person LWimsey; 30.11.2017
comment
Хорошо, извините, я продолжаю упоминать оборудование и asm :) Я посмотрел, как стандарт описывает требования, но я не искал различий между тем, что он подразумевает / требует, и тем, что может гарантировать модель с односторонним барьером. Как я уже сказал, я думаю, что на самом деле это то же самое, поэтому можно с уверенностью описать гарантию в стандарте как одностороннее препятствие, если все четко понимают, какой именно барьер мы ' мы говорим о, потому что этот термин используется во многих контекстах. : P Конечно, правило «как если бы» означает, что реализация может работать как угодно. - person Peter Cordes; 30.11.2017
comment
@PeterCordes Я полностью согласен .. Определенно безопасно использовать односторонний барьерный подход для описания того, как все работает. Это также (по крайней мере, для меня) самый простой способ визуализировать вещи. - person LWimsey; 01.12.2017
comment
А, значит, вы говорите, что только юрист, придерживающийся догматического языка, будет возражать против этого. Понятно: P - person Peter Cordes; 01.12.2017
comment
Я бы не сказал, что аппаратные барьеры всегда двусторонние, хотя это зависит от того, что вы имеете в виду. Канонические барьеры SPARC, безусловно, асимметричны в том смысле, что они применяют разные правила в зависимости от направления, в котором операция будет перемещаться через барьер. Таким образом, получение нагрузки может быть реализовано с помощью простой загрузки, за которой следуют барьеры LoadLoad и LoadStore. Это создает более или менее односторонний барьер (более ранние склады могут свободно пересекать, а более ранние грузы могут пересекать до следующей загрузки). - person BeeOnRope; 01.12.2017
comment
@PeterCordes Я бы не сказал, что аппаратные барьеры всегда двусторонние, хотя это зависит от того, что именно вы подразумеваете под двумя способами. Канонические барьеры в стиле SPARC, такие как StoreLoad, безусловно, асимметричны в том смысле, что они применяют разные правила в зависимости от направления, в котором операция будет перемещаться через барьер. Таким образом, получение нагрузки может быть реализовано с помощью простой загрузки, за которой следуют барьеры LoadLoad и LoadStore. Это создает более или менее односторонний барьер (более ранние склады могут свободно пересекать, а более ранние грузы могут пересекать до следующей загрузки). - person BeeOnRope; 01.12.2017
comment
@BeeOnRope: хороший момент, я не думал только о LoadStore перед тем, как открыть магазин. Это своего рода одностороннее движение. А также интересный момент о пересечении более ранних нагрузок до следующей загрузки с барьером load + LoadLoad. Но это все еще двухсторонний барьер нагрузки, он просто не привязан к загрузке. - person Peter Cordes; 01.12.2017
comment
Но что вы подразумеваете под двусторонним барьером? Моя интерпретация двухстороннего подхода заключалась бы в том, что одни и те же правила будут применяться в обоих направлениях, например, если магазины не могут пересечь барьер в одну сторону (т. Е. Старые магазины мигрируют до барьера), то они не могут пересечь другой путь. или. Таким образом, асимметричный барьер (например, позволяющий магазинам перемещаться в одну сторону, но не в другую) не может считаться двусторонним барьером. Это не означает, что это односторонний барьер: это асимметричный барьер, который ограничивает движение в обе стороны, но в противоположных направлениях. Вот такой асимметричный барьер, например, мощность lwsync. - person BeeOnRope; 01.12.2017
comment
@jacobsa Вы должны понимать, что в программе MT чтение - это действительно не запись. Мне потребовалось время, чтобы осознать это. Вы не можете просматривать чтение как запись. Записи - это модификации, которые являются порядком модификации; читает, использует порядок модификации и не вносит в него свой вклад. Чтения зависят от записи, а не от других чтений. Если бы чтение было записью, вы бы получили совершенно другую модель. Фактически, если вы замените каждое чтение на RMW, вы получите очень сильную модель. (Со значением, защищенным мьютексом, это, по сути, то, что у вас есть.) - person curiousguy; 28.11.2019