Ослабленная производительность упорядочивания x86?

Поскольку Intel предоставляет надежную аппаратную модель памяти, есть ли вообще какое-то преимущество в использовании memory_order_relaxed в программе на C ++ 11? Или просто оставьте значение по умолчанию «последовательное согласование», поскольку это не имеет значения?


person excalibur    schedule 20.01.2015    source источник
comment
Перефразируя Страуструпа, нестандартные порядки предназначены для экспертов.   -  person DanielKO    schedule 20.01.2015
comment
Обратной стороной использования таких вещей, как memory_ordered_relaxed на x86, является то, что вы знаете, будет ли код по-прежнему правильным при переносе на другую архитектуру, потому что вы не можете протестировать его, пока не портируете.   -  person Nevin    schedule 22.01.2015
comment
Также стоит упомянуть, что другие порядки памяти, помимо ослабления, также действуют как барьеры компилятора, предотвращая переупорядочение доступа к памяти во время оптимизации.   -  person Pezo    schedule 15.06.2019
comment
Отвечает ли это на ваш вопрос? Порядок памяти: потреблять, acq_rel и seq_cst когда-либо требовались на Intel x86?   -  person user    schedule 23.01.2021
comment
См. Также этот ответ.   -  person user    schedule 01.02.2021


Ответы (2)


Как и на большинство других ответов в области информатики, ответ на этот вопрос - «это зависит от обстоятельств».

Во-первых, идея о том, что последовательное последовательное упорядочение никогда не влечет за собой никаких штрафов, неверна. В зависимости от вашего кода (и, возможно, компилятора) он может и будет нести штраф.

Во-вторых, чтобы принимать разумные решения об ограничениях порядка памяти, вам нужно подумать (и понять), как вы используете задействованные данные.

memory_order_relaxed полезен для чего-то вроде автономного счетчика, который должен быть атомарным, но не связан напрямую с чем-то еще, поэтому он не должен согласовываться с каким-либо «чем-то еще». Типичным примером может служить счетчик ссылок, например, в shared_ptr или некоторых более старых реализациях std::string. В этом случае нам просто нужно убедиться, что счетчик увеличивается и уменьшается атомарно, и что его изменения видны всем потокам. Но, в частности, нет никаких связанных данных, с которыми он должен оставаться согласованным, поэтому нас не заботит его упорядочение по отношению к чему-либо еще.

Последовательно согласованный порядок в значительной степени находится на противоположной крайности. Это, вероятно, самый простой в применении - вы пишете код примерно так, как будто он однопоточный, и реализация гарантирует, что он работает правильно (это не значит, что вам вообще не нужно учитывать потоки, но последовательно согласованный порядок обычно требует наименьшего количества размышлений, но также обычно является самой медленной моделью).

Согласованность получения / выпуска обычно используется, когда у вас есть два или более связанных фрагмента информации, и вам нужно убедиться, что один становится видимым только до / после другого. В качестве примера, с которым я имел дело недавно, предположим, что вы создаете что-то вроде базы данных в памяти. У вас есть некоторые данные и некоторые метаданные (и вы храните их более или менее отдельно).

Метаданные используются (среди прочего) для поиска в базе данных. Мы хотим быть уверены, что если кто-то найдет какие-то конкретные данные, они действительно будут присутствовать в базе данных.

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

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

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

person Jerry Coffin    schedule 20.01.2015
comment
mo_seq_cst по-прежнему требует дополнительных затрат на x86 для операций с чистым хранилищем, например var = 1! Это полный барьер памяти (например, использование xchg для хранилища seq-cst или, что еще хуже, mov + mfence вместо простого mov). На x86 mo_release / mo_acquire бесплатно в asm по сравнению с mo_relaxed. Единственная цена (с текущими компиляторами) - это, возможно, блокировка переупорядочения во время компиляции с неатомарными переменными. - person Peter Cordes; 15.06.2019
comment
@PeterCordes: Да, то, что я изначально написал, было основано исключительно на последствиях, которые могли бы возникнуть, если бы предположение ОП было верным. К сожалению, это не так, поэтому большая часть ответа была неверной. Я переписал (как мне кажется), чтобы сделать это немного точнее. - person Jerry Coffin; 16.06.2019

Всегда используйте минимальные гарантии, необходимые для того, чтобы ваш код был правильным.

Ни больше, ни меньше.

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

Конечно, если вы уверены, что никогда не будете заботиться о переносе какого-либо кода, принятие более строгих гарантий там, где вы знаете, что это не будет иметь значения на ваших платформах, может упростить доказательство его правильности.
Чем труднее злоупотреблять, тем проще рассуждать или короче - тоже вполне приемлемые причины для использования менее эффективных конструкций.

person Deduplicator    schedule 20.01.2015
comment
Если у вас нет слабо упорядоченного ISA для тестирования, возможно, опасно использовать более слабый порядок, который вы не можете протестировать. mo_release / mo_acquire / mo_acq_rel - самый слабый, который вы можете протестировать на x86. (Но для чистых магазинов дешевле, чем mo_seq_cst.) - person Peter Cordes; 15.06.2019