Нет, я так не думаю. Это не ожидание вращения. Он не ждет, пока другой поток сохранит 0
или что-то еще. имеет смысл иметь смысл попробовать снова сразу после сбоя lock cmpxchg
, а не в режиме сна ~ 100 циклов (на Skylake и более поздних) или ~ 5 циклов (на более ранних процессорах Intel).
Если lock cmpxchg
завершить вообще (успешно или неудачно), это означает, что строка кэша теперь находится в состоянии Modified (или, может быть, просто исключено?) В этом ядре, поэтому прямо сейчас самое время попробовать еще раз.
Реальные сценарии использования атомных соединений без блокировки обычно не чрезвычайно тяжело утверждаются, иначе вы должны обычно использовать запасной вариант для сна / пробуждения с помощью ОС.
(Но если есть конкуренция, есть аппаратный арбитраж для lock
инструкции ред, в высшей степени утверждали случае, если я не знаю, если это, вероятно, для ядра, чтобы выполнить 2-й * инструкцию 1023 * ред до потери снова строка кэша. Но, надеюсь, да.)
lock cmpxchg
не может внезапно потерпеть неудачу, поэтому фактическая живая блокировка невозможна: по крайней мере одно ядро добьется прогресса, если его CAS преуспеет в алгоритме, подобном этому, для раунда каждого ядра, имеющего ход. В архитектуре LL / SC compare_exchange_weak
может внезапно давать сбой, поэтому переносимость на не-x86 может потребовать заботы о livelock, в зависимости от деталей реализации, но я думаю, что это маловероятно. (И, конечно, _mm_pause
, в частности, только x86.)
Другая причина использования pause
состоит в том, чтобы избежать ошибочных предположений порядка памяти при выходе из цикла ожидания с вращением, который вращается только для чтения, ожидая, пока замок не будет разблокирован, прежде чем пытаться атомарно заявить о нем. (Это лучше, чем вращаться на xchg
или lock cmpxchg
и иметь все ожидающие потоки, забивающие строку кэша.)
Но это опять-таки не проблема, потому что цикл повторов уже включает lock cmpxchg
, который является полным барьером, а также атомарный RMW, поэтому я думаю, что это позволяет избежать ошибочных предположений порядка памяти.
Особенно, если вы пишете цикл эффективно / правильно для использования результата загрузки сбоя cmpxchg при повторных попытках, удаляя чистую нагрузку var
из цикла .
Это канонический способ построения произвольной атомарной операции из примитива CAS. compare_exchange_weak
обновляет свой первый аргумент, если сравнение не удается, поэтому вам не нужна другая загрузка внутри цикла.
#include <atomic>
int atomicLeftShift(std::atomic<int>& var, int shiftBy)
{
int expected = var.load(std::memory_order_relaxed);
int desired;
do {
desired = expected << shiftBy;
} while( !var.compare_exchange_weak(expected, desired) ); // seq_cst
return desired;
}
компилируется с clang7.0 -O3 для x86-64 в этот ассемблер в проводнике компилятора Godbolt:
atomicLeftShift(std::atomic<int>&, int):
mov ecx, esi
mov eax, dword ptr [rdi] # pure load outside the loop
.LBB0_1: # do {
mov edx, eax
shl edx, cl # desired = expected << count
lock cmpxchg dword ptr [rdi], edx # eax = implicit expected, updated on failure
jne .LBB0_1 # } while(!CAS)
mov eax, edx # return value
ret
Единственный доступ к памяти в повторном цикле - lock cmpxchg
, который не может пострадать из-за неправильного предположения порядка памяти. По этой причине pause
не требуется.
Также не требуется pause
для простой задержки отката, кроме случаев, когда у вас много споров и вы хотите, чтобы один поток делал несколько вещей подряд с одной и той же общей переменной для увеличения пропускной способности. то есть отключить другие потоки в том редком случае, когда cmpxchg
терпит неудачу.
Это only имеет смысл, если нормально, когда один поток выполняет несколько атомарных операций в строке над одной и той же переменной (или одной в той же строке кэша, если у вас есть проблемы с ложным разделением), вместо помещая больше операций в одну CAS-попытку.
Это, вероятно, редко встречается в реальном коде, но часто встречается в синтетическом микробенчмарке, где вы позволяете нескольким потокам многократно отбрасывать общую переменную без какой-либо другой работы между ними.