Должна ли загрузка-приобретение немедленно увидеть релиз-магазин?

Предположим, у нас есть одна простая переменная (std::atomic<int> var) и 2 темы T1 а также T2 и у нас есть следующий код для T1:

...
var.store(2, mem_order);
...

и для T2

...
var.load(mem_order)
...

Также давайте предположим, что T2(нагрузка) выполняет 123 нс позже во времени(позже в порядке изменения в терминах стандарта C ++), чем T1(хранить).
Мое понимание этой ситуации заключается в следующем (для разных порядков памяти):

  1. memory_order_seq_cstT2 груз обязан загрузить 2, Так эффективно он должен загружать последнее значение (как в случае с операциями RMW)
  2. memory_order_acquire/memory_order_release/memory_order_relaxedT2 не обязан загружать 2 но может загружать любое старое значение с единственным ограничением: это значение не должно быть старше, чем последнее, загруженное этим потоком. Так, например var.load возвращается 0,

Я прав с моим пониманием?

Update1:

Если я ошибаюсь в рассуждениях, предоставьте текст из стандарта C ++, который подтверждает это. Не только теоретическое обоснование того, как может работать какая-то архитектура.

6

Решение

Я прав с моим пониманием?

Нет. Вы неправильно понимаете заказы памяти.

давайте предположим, что T2(загрузка) выполняет на 123 нс позже, чем T1(хранить)…

В этом случае T2 увидит, что T1 делает с любым типом заказов памяти (более того, это свойство применяется для чтения / записи любой области памяти, см., Например, http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4431.pdf, 1.10, с.15). Ключевое слово в вашей фразе потом: это означает, что кто-нибудь другой заставляет упорядочивать эти операции.

Заказы памяти используются для другого сценария:

Позволяет некоторую операцию OP1 приходит в потоке T1 перед операцией магазина, OP2приходит после этого, OP3 приходит в потоке T2 перед нагрузкой, OP4 приходит после этого.

//T1:                         //T2:
OP1                           OP3
var.store(2, mem_order)       var.load(mem_order)
OP2                           OP4

Предположим, что некоторый порядок между var.store() а также var.load() можно наблюдать по темам. Что можно гарантировать порядок перекрестных потоков других операций?

  1. Если var.store использования memory_order_release, var.load использования memory_order_acquire а также var.store заказан до var.load (то есть нагрузка возвращает 2), затем эффект OP1 заказан до OP4,

Например, если OP1 пишет некоторую переменную var1, OP4 читает эту переменную, то можно быть уверенным, что OP4 прочтем что OP1 пиши раньше. Это наиболее используемый случай.

  1. Если оба var.store а также var.load использования memory_order_seq_cst а также var.store заказан после var.load (то есть load возвращает 0, которое было значением переменной до сохранения), затем эффект OP2 заказан после OP3,

Этот порядок памяти требуется некоторыми хитрыми схемами синхронизации.

  1. Если либо var.store или же var.load использования memory_order_relaxedто с любым порядком var.store а также var.load можно гарантировать нет заказа операций перекрестных потоков.

Этот порядок памяти используется в случае, когда кто-нибудь другой обеспечить порядок операций. Например, если нить T2 творение приходит после var.store в T1, затем OP3 а также OP4 заказаны после OP1,

ОБНОВИТЬ: 123 ns later подразумевает *someone else* force ordering потому что процессор компьютера не имеет понятия об универсальном времени, и ни одна операция не имеет точный момент когда это будет выполнено. Для измерения времени между двумя операциями вы должны:

  1. Соблюдайте порядок между окончанием первой операции и началом отсчета времени на какой-то процессор.
  2. Соблюдайте порядок между начальным и конечным счетными операциями.
  3. Соблюдайте порядок между операцией подсчета времени окончания и началом второй операции.

Переходно, эти шаги делают упорядочивание между первой операцией и второй.

4

Другие решения

Не найдя аргументов, доказывающих, что мое понимание неверно, я считаю его правильным, и мое доказательство состоит в следующем:

memory_order_seq_cst — загрузка T2 обязана загрузить 2.

Это правильно, потому что все операции, использующие memory_order_seq_cst должен формировать единый общий порядок по атомарной переменной всех операций с памятью.
Выдержка из стандарта:

[29.9 / 3] На всех элементах memory_order_seq_cst должен быть один общий порядок S
операции, соответствующие порядку «происходит раньше» и
заказы на изменение для всех затронутых местоположений, так что каждый
Операция memory_order_seq_cst B, которая загружает значение из атомарного
объект М наблюдает одно из следующих значений <…>

Следующий пункт моего вопроса:

memory_order_acquire / memory_order_release / memory_order_relaxed — T2 — это
не обязан загружать 2, но может загрузить любое старое значение <…>

Я не нашел никаких доказательств, которые могли бы указывать, что нагрузка, выполненная позже в порядке модификации, должна видеть последнее значение. Единственные точки, которые я нашел для операций сохранения / загрузки с любым порядком памяти, отличным от memory_order_seq_cst эти:

[29.3 / 12] Реализации должны сделать атомарные хранилища видимыми для атомарных
загружается в течение разумного периода времени.

а также

[1.10 / 28] Реализация должна гарантировать, что последнее значение (в порядке изменения) назначено атомарной операцией или операцией синхронизации
станет видимым для всех других потоков за конечный период времени.

Таким образом, единственная гарантия, которую мы имеем, состоит в том, что записанная переменная будет видна в течение некоторого времени — это довольно разумная гарантия, но она не подразумевает немедленной видимости предыдущего хранилища. И это подтверждает мою вторую мысль.

Учитывая все это, мое первоначальное понимание было правильным.

1