К сожалению, это распространенное заблуждение об атомных порядках памяти. Видите ли, эти не (полностью) применимы к реальной атомарной операции. Они применяются в основном к другим операциям вокруг них.
Например:
//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
mfence
: он не может сделать запись видимой или отправить ее в память быстрее, поскольку ядро всегда пытается отправить записи в свой кеш, откуда они видны глобально. Но эта инструкция предотвращает получение более старых значений при последующих чтениях. (Это может быть достигнуто либо путем ожидания записи в кеш, либо путем выполнения чтения как можно скорее, а затем двойной проверки их достоверности позже.) - person curiousguy   schedule 07.06.2019mfence
очищает буфер хранилища, поэтому последующие чтения не будут выполняться до тех пор, пока хранилища не станут глобально видимыми ... - person LWimsey   schedule 07.06.2019cout
, буфер хранилища должен быть очищен, чтобы иметь видимый эффект. Это сильно вводит в заблуждение: в отличие от файлового буфера, буфер хранилища ЦП очищается прямо сейчас. - person curiousguy   schedule 07.06.2019mfence
вызывает очистку буфера хранилища перед выполнением следующих инструкций (загрузки). Это сделано для предотвращения переупорядочения хранилищ и загрузок (например, барьер #StoreLoad). Если вы не очистите буфер хранилища, хранилища все равно будут сохранены в памяти, но, возможно, после того, как загрузки были выполнены, и это проблема для некоторых алгоритмов. В руководстве Intel это называется «сериализацией» операций загрузки и сохранения. - person LWimsey   schedule 07.06.2019mfence
останавливает выполнение до тех пор, пока хранилища не будут сброшены, но я не думаю, что это что фактически гарантируется в будущих процессорах. - person curiousguy   schedule 07.06.2019mfence
: Он гарантирует, что все загрузки и сохранения, указанные перед забором, глобально наблюдаемый до любых грузов или складирования, выполняемых после забора .. - person LWimsey   schedule 07.06.2019