Безопасно ли вытеснение C ++ Atomics? - PullRequest
0 голосов
/ 21 июня 2020

Насколько я понимаю, это специальные инструкции по сборке, которые гарантируют, что два процессора в системе SMP не могут записывать в одну и ту же область памяти одновременно. Например, в PowerP C приращение atomi c будет выглядеть примерно так:

retry:
  lwarx  r4, 0, r3 // Read integer from RAM location r3 into r4, placing reservation.
  addi   r4, r4, 1 // Add 1 to r4.
  stwcx. r4, 0, r3 // Attempt to store incremented value back to RAM.
  bne-   retry     // If the store failed (unlikely), retry.

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

Судя по тому, что я вижу для C ++ Atomics , они, похоже, при необходимости применяют блокировки. Итак, мой первый вопрос -

  1. Гарантирует ли стандарт C ++ отсутствие вытеснения во время операции atomi c? Если да, то где в стандарте это найти?

Я проверил atomic<int>::is_always_lock_free на своем Intel P C, и он получил true. С моим предположением о вышеупомянутом сборочном блоке это меня смутило. Покопавшись в сборке Intel (с которой я не знаком), я обнаружил, что происходит lock xadd DWORD PTR [rdx], eax. Итак, мой вопрос -

Есть ли в некоторых архитектурах инструкции, относящиеся к atomi c, которые гарантируют отсутствие вытеснения? Или я неправильно понимаю?

Наконец, мне было интересно узнать о семантике compare_exchange_weak и compare_exchange_strong -

Есть ли разница l ie int в механизме повтора или это что-то еще?

EDIT : После прочтения ответов мне интересно еще кое-что

Операции функций-членов atomi c fetch_add, operator++ et c. они сильные или слабые?

Ответы [ 2 ]

1 голос
/ 22 июня 2020

Это похоже на этот вопрос: Все, что в std :: atomi c не требует ожидания?

Вот некоторые определения свободы блокировки и свободы ожидания (оба взято из Википедии ):

Алгоритм lock-free , если, когда потоки программы выполняются в течение достаточно длительного времени, при По крайней мере, один потоков выполняется.

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

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

Что касается свободы от блокировок, не имеет значения, может ли поток быть вытеснен в середине операции atomi c или нет.

Некоторые операции можно перевести на сингл e atomi c операция, в этом случае это операция без ожидания и поэтому не может быть прервана на полпути. Однако то, какие операции фактически не требуют ожидания, зависит от компилятора и целевой архитектуры (как описано в моем ответе на упомянутый вопрос SO).

Что касается разницы между compare_exchange_weak и compare_exchange_strong - слабые версия может ошибаться ложно, т. е. может не работать, даже если сравнение действительно верно. Это может произойти на архитектурах с LL / S C. Предположим, мы используем compare_exchange_weak, чтобы обновить некоторую переменную до ожидаемого значения A . LL загружает значение A из переменной, и перед выполнением S C переменная изменяется на B , а затем обратно на A . Таким образом, даже если переменная содержит то же значение, что и раньше, промежуточное изменение на B вызывает сбой S C (и, следовательно, compare_exchange_weak). compare_exchange_strong не может ошибаться ложно, но для этого необходимо использовать retry-l oop на архитектурах с LL / S C.

Я не совсем уверен, что вы имеете в виду под fetch_add быть «сильным или слабым». fetch_add не может сбой - он просто выполняет atomi c обновление некоторой переменной, добавляя предоставленное значение, и возвращает старое значение переменной. Может ли это быть преобразовано в одну инструкцию (как на Intel) или в повторную попытку l oop с LL / S C (Power) или CAS (Spar c), зависит от целевой архитектуры. В любом случае, переменная будет обновляться правильно.

1 голос
/ 21 июня 2020

Гарантирует ли стандарт C ++ отсутствие вытеснения во время операции atomi c? Если да, то где в стандарте я могу это найти?

Нет, нет. Поскольку на самом деле код не может определить, произошло это или нет (это неотличимо от упреждения как до, так и после операции atomi c, в зависимости от обстоятельств), для этого нет никаких оснований. * Есть ли в некоторых архитектурах инструкции, относящиеся к atomi c, которые гарантируют отсутствие вытеснения? Или я неправильно понимаю?

В этом не было бы никакого смысла, так как операция должна выглядеть как atomi c в любом случае, поэтому упреждение во время всегда будет идентично наблюдаемому поведению с упреждением ранее или после. Если вы можете написать код, который когда-либо видит случай, когда упреждение во время операции atomi c вызывает наблюдаемые эффекты, отличные от упреждения до или после упреждения, эта платформа не работает, поскольку операция не ведет себя атомарно *. 1011 *

...