memory_order_relaxed и атомарные операции RMW

В стандарте C ++ говорится, что операции RMW (чтение-изменение-запись) на атомике будут работать с последним значением атомарной переменной. Следовательно, использование memory_order_relaxed с этими операциями не повлияет на операцию RMW при одновременном выполнении из нескольких потоков.

Я предполагаю, что такое поведение возможно только при наличии некоторого барьера или ограждения памяти для операций RMW, даже если указанный порядок памяти "ослаблен". Пожалуйста, поправьте меня, если я неправильно понимаю, и объясните, как эти операции работают с последним значением, если такой барьер памяти не используется. Если я правильно понимаю, то могу ли я предположить, что использование порядка памяти Acquire-Release или Seq-CST не должно приводить к дополнительному снижению производительности для операций RMW, скажем, на слабо упорядоченной архитектуре, такой как ARM или Alpha. Заранее спасибо.


person MS Srikkanth    schedule 06.11.2017    source источник
comment
Наверняка реализация зависит от платформы и ОС   -  person Ed Heal    schedule 06.11.2017
comment
Да, это зависит от реализации, но как насчет второй части моего вопроса - будет ли использование seq CST или acq-rel с этими операциями какого-либо дополнительного воздействия на производительность по сравнению с упрощенным порядком с учетом требований стандарта?   -  person MS Srikkanth    schedule 06.11.2017
comment
Я не знаю. Вы это измерили? Это проблема производительности вашей системы?   -  person Ed Heal    schedule 06.11.2017
comment
Независимо от порядка памяти, RMW гарантированно работают с последними в порядке модификации. Упорядочение памяти более сильное, чем 'ослабленное', определяет, как другие операции упорядочиваются между потоками по отношению к самому RMW.   -  person LWimsey    schedule 06.11.2017
comment
@Ed Heal - Прошу дать общие сведения, поскольку мы будем переносить нашу кодовую базу на процессор ARM v7, и я хотел бы узнать о влиянии на производительность, если таковое имеется. Я еще не профилировал.   -  person MS Srikkanth    schedule 06.11.2017
comment
@LWimsey - разве условие, которое работает с последним значением, не требует чего-то вроде барьера памяти? При наличии барьера памяти это будет автоматически означать, что тогда другие межстадийные операции будут автоматически упорядочены относительно барьера памяти и, следовательно, фактически они будут упорядочены относительно самой операции RMW. Вот почему мне любопытно, окажут ли Seq-CST или acq-rel дополнительное влияние на производительность.   -  person MS Srikkanth    schedule 06.11.2017
comment
@Madhusudhan Это частая ошибка, но этого не произойдет. Барьеры (или ограждения) сохраняют порядок программ и делают их видимыми для других процессоров.   -  person LWimsey    schedule 06.11.2017
comment
@EdHeal Вы не сможете измерить это на типичном ПК, поскольку x86 имеет жесткие барьеры памяти для всех инструкций RMW (блокировка-префикс). Таким образом, нет никакой разницы между последовательной согласованностью и получением-выпуском. OTOH, ослабленная семантика позволит компилятору перемещать другие вещи через сам RMW.   -  person Mysticial    schedule 06.11.2017
comment
@LWimsey Барьеры, как правило, не делают материал видимым для другого процессора, они предотвращают отображение некоторых материалов до тех пор, пока не станут видны другие.   -  person curiousguy    schedule 07.06.2019
comment
@Mysticial разве условие, которое работает с последним значением, не требует чего-то вроде барьера памяти? Нет, поскольку работа с чем-либо еще просто невозможна. Вы можете только прочитать последнее значение в реальном времени и записать последнее значение. ЦП не путешествует во времени (как и память). Последний определен только для одной ячейки памяти. Последние значения значений бессмысленны и сбивают с толку, они просто говорят, что операция атомарна: последняя до модификации.   -  person curiousguy    schedule 07.06.2019
comment
@curiousguy хм, да, можно так сказать ... Я не помню, как я думал тогда, но это также зависит от того, были ли упомянутые ограждения инструкциями по ограждению процессора или ограждениями в модели памяти C ++   -  person LWimsey    schedule 07.06.2019
comment
@LWimsey f.ex. Intel mfence: он не может сделать запись видимой или отправить ее в память быстрее, поскольку ядро ​​всегда пытается отправить записи в свой кеш, откуда они видны глобально. Но эта инструкция предотвращает получение более старых значений при последующих чтениях. (Это может быть достигнуто либо путем ожидания записи в кеш, либо путем выполнения чтения как можно скорее, а затем двойной проверки их достоверности позже.)   -  person curiousguy    schedule 07.06.2019
comment
@curiousguy Это один из способов выразиться. mfence очищает буфер хранилища, поэтому последующие чтения не будут выполняться до тех пор, пока хранилища не станут глобально видимыми ...   -  person LWimsey    schedule 07.06.2019
comment
@LWimsey очищает буфер хранилища, похоже, предполагает, что, как и cout, буфер хранилища должен быть очищен, чтобы иметь видимый эффект. Это сильно вводит в заблуждение: в отличие от файлового буфера, буфер хранилища ЦП очищается прямо сейчас.   -  person curiousguy    schedule 07.06.2019
comment
@curiousguy mfence вызывает очистку буфера хранилища перед выполнением следующих инструкций (загрузки). Это сделано для предотвращения переупорядочения хранилищ и загрузок (например, барьер #StoreLoad). Если вы не очистите буфер хранилища, хранилища все равно будут сохранены в памяти, но, возможно, после того, как загрузки были выполнены, и это проблема для некоторых алгоритмов. В руководстве Intel это называется «сериализацией» операций загрузки и сохранения.   -  person LWimsey    schedule 07.06.2019
comment
@LWimsey Он ожидает, пока буфер не станет пустым (сохраненные данные зафиксированы), чтобы зафиксировать следующую загрузку: не допускается загруженное значение, которое не является актуальным по сравнению с этими хранилищами. (Но оптимистичное ядро ​​все еще может выполнять загрузки на ранних этапах спекулятивного выполнения, пока значения снова проверяются в конце.) В документации может быть указано, что mfence останавливает выполнение до тех пор, пока хранилища не будут сброшены, но я не думаю, что это что фактически гарантируется в будущих процессорах.   -  person curiousguy    schedule 07.06.2019
comment
Вы можете сказать, что он сбрасывает sb, или он ждет, пока sb будет сброшен, но эффект тот же ... Вот что говорится в руководстве Intel о mfence: Он гарантирует, что все загрузки и сохранения, указанные перед забором, глобально наблюдаемый до любых грузов или складирования, выполняемых после забора ..   -  person LWimsey    schedule 07.06.2019


Ответы (1)


К сожалению, это распространенное заблуждение об атомных порядках памяти. Видите ли, эти не (полностью) применимы к реальной атомарной операции. Они применяются в основном к другим операциям вокруг них.

Например:

//accessible from anywhere
std::atomic<bool> flag;
int value = 0;

//code in thread 1:
value = 1;
flag.store(true, <order_write>);

//code in thread 2:
bool true_val = true;
while(!flag.compare_exchange_weak(true_val, false, <order_read>);
int my_val = value;

Итак, что это делает? Поток 2 ожидает от потока 1 сигнала об обновлении value, затем поток 2 читает value.

<order_write> и <order_read> не управляют поведением видения конкретной атомарной переменной. Он определяет, как будут отображаться другие значения, которые были установлены до и после этой атомарной операции.

Чтобы этот код работал, <order_write> должен использовать порядок памяти, по крайней мере, такой же сильный, как memory_order_release. И <order_read> должен использовать порядок памяти не ниже memory_order_acquire.

Эти порядки памяти влияют на то, как value передается (или, более конкретно, на материал, установленный перед атомарной записью).

Разве условие, что «работать с последним значением» не требует чего-то вроде барьера памяти?

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

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

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

Возможно ли для атомарных реализаций использовать полный глобальный барьер памяти для любой атомарной операции? да. Но если реализация прибегает к этому для фундаментальных атомарных типов, то у архитектуры, вероятно, нет другого выбора. Так что, если ваш алгоритм требует атомарных операций, у вас действительно нет другого выбора.

person Nicol Bolas    schedule 06.11.2017
comment
Мне уже известна первая часть вашего ответа. Возможно, в своем вопросе мне следовало пояснить, что я имел в виду, что использование барьера заставит расслабленное упорядочение вести себя аналогично acq-rel. Однако я не знаком с барьером локальной памяти. Я прочитаю об этом больше и вернусь сюда после этого. - person MS Srikkanth; 06.11.2017
comment
@Madhusudhan В C ++ нет такого понятия, как локальный или глобальный барьер памяти - person LWimsey; 07.11.2017
comment
@LWimsey: Если вы хотите получить техническую информацию, в C ++ нет никакого барьера памяти. Моя терминология здесь просто для обозначения различий между тем, что CPU / компилятор должен делать, чтобы сделать доступным конкретный адрес памяти, и тем, что должен сделать CPU / компилятор, чтобы сделать доступными все адреса памяти. Настоящая атомная РМВ - первая; последними являются операции упорядочивания памяти. - person Nicol Bolas; 07.11.2017
comment
Стандарт C ++ называет их заборами - person LWimsey; 07.11.2017