Короткий ответ: какой бы ни была ваша проблема, неупорядоченная вряд ли будет решением проблемы!
Более длинный ответ ...
... Справочное руководство по языку LLVM гласит:
неупорядочено
Набор значений, которые могут быть прочитаны, регулируется происходит-до частичный заказ. Значение не может быть прочитано, если какая-то операция не написала его. Это предназначено для обеспечения достаточно сильной гарантии для моделирования энергонезависимых общих переменных Java. Этот порядок нельзя указывать для операций чтение-изменение-запись ; оно недостаточно сильное, чтобы сделать их атомами c любым интересным способом.
«Значение не может быть прочитано, если какая-то операция его не записала». это весело ! Это означает, что «спекулятивные» записи не допускаются. Допустим, у вас есть if (y == 99) x = 0 ; else x = y+1 ;
: оптимизатор может превратить это в x = y+1 ; if (y == 99) x = 0 ;
, где первая запись x
является «спекулятивной». (Я не говорю, что это разумная или обычная оптимизация. Дело в том, что преобразования, которые совершенно нормальны с точки зрения отдельного потока, не подходят для атомарных процессов.) Стандарты C / C ++ имеют то же ограничение: нет «out» допустимы значения "из воздуха".
В других местах документация LLVM описывает неупорядоченные как нагрузки / хранилища, которые выполняются без перерыва из любого другого хранилища - так нагрузка, которая читает две половины (скажем) значения, будет не квалифицироваться, если две половины могут быть результатом двух отдельных записей!
Кажется монотонным c является локальным именем для C / C ++ memory_order_relaxed
и описывается:
monotoni c
В дополнение к гарантиям неупорядоченного , есть единый общий заказ на модификации по монотонным c операциям на каждом адресе. Все заказы на модификацию должны быть совместимы с заказом случай-до .
В отличие от неупорядоченного , с monotoni c все потоки будут видеть записи по данному адресу в том же порядке. Это означает, что если поток «a» записывает «1» в заданное место, а затем поток «b» пишет «2», то после этого потоки «c» и «d» должны оба прочитать «2». (Отсюда и название.)
Нет никакой гарантии, что заказы на изменение могут быть объединены в общий общий заказ для всей программы (а это часто будет невозможно).
Это расслабленный бит.
Операция чтения в атоме c операция чтения-изменения-записи (cmpxchg
и atomicrmw
) читает значение в порядке изменения непосредственно перед записываемым значением.
То же, что C / C ++: чтение-изменение-запись не может быть прервано другой записью .
Если одно чтение атома c происходит до того, как другое чтение атома c произойдет по тому же адресу, при последующем чтении должно быть то же значение или более позднее значение в порядке изменения адреса. Это запрещает изменение порядка однообразных c (или более сильных) операций с одним и тем же адресом.
Это однообразное c, ребята.
Если адрес написан однообразно c -только одним потоком, а другие потоки монотонно c -для чтения этого адреса несколько раз, другие потоки должны в конечном итоге увидеть запись.
Так что ... может быть некоторая задержка между записью и чтением, но эта задержка конечна (но, я полагаю, не может быть одинаковой для всех потоков). «В конце концов» интересно. Стандарт C11 гласит: «Реализации должны сделать хранилища atomi c видимыми для загрузок atomi c в течение разумного промежутка времени», что аналогично, но с более позитивным вращением: -)
Это соответствует C ++ 0x / C1x memory_order_relaxed
.
Итак, вы go.
Вы спросили:
- Скажем, у меня атоми c bool, безопасно ли изменять его через неупорядоченную загрузку / хранение?
- Скажем, у меня есть атомарная c битовая маска, безопасно ли изменять ее через неупорядоченную загрузку / хранилище?
Это скорее зависит от того, что вы подразумеваете под safe :-( С любым атомом c загрузкой 'x', за которой следует атоми c хранилище 'x', у вас есть без понятия сколько других магазинов до 'x с момента загрузки. Но добавленная радость неупорядоченного заключается в том, что он не гарантирует, что все потоки будут видеть все записи в 'x' в том же порядке!
Ваш другие вопросы спорны, потому что вы не можете иметь неупорядоченные операции чтения-изменения-записи.
На практике ваш x86_64 гарантирует, что все записи видны всем потокам в одном и том же порядке - - так что все операции atomi c выполняются как минимум monotoni c / memory_order_relaxed
- нет префиксов LOCK
и никаких инструкций xFENCE
не требуется, просто MOV
для памяти будет сделать трюк. На самом деле, лучше, чем это, простой MOV
в / из памяти дает memory_order_release
/ memory_order_acquire
. * 113 1 *
FWIW: вы упоминаете побитовые операции. Я думаю, очевидно, что чтение / запись нескольких битов будет включать чтение / запись некоторого количества других битов в качестве побочного эффекта. Что добавляет веселья.
Я предполагаю, что как минимум вам нужно будет выполнять операции чтения-изменения-записи. Опять же, на x86_64 это сводится к инструкции с префиксом LOCK
, которая обойдется в 10 секунд - сколько из них зависит от процессора и степени конкуренции. Теперь блокировка и разблокировка mutex
будут включать редактирование LOCK
, поэтому обычно стоит заменить mutex_lock / ... выполнить чтение и запись ... / mutex_unlock на атомом c read-modify- запись (и) действительно только в том случае, если это только одна операция чтения-изменения-записи.
Недостаток мьютекса, конечно, в том, что поток может быть "заменен", пока он содержит мьютекс: - (
Для спин-блокировки (на x86_64) требуется LOCK
для получения, но не для отпускания ... но эффект "вытаскивания" при удерживании спин-блокировки еще хуже: - (