Может ли кто-нибудь дать мне краткое изложение преимуществ и недостатков Сравнить и поменять местами программирование? (например, производительность многоядерного процессора)
Вот и пример на Java:
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
=== РЕДАКТИРОВАТЬ ===
Пожалуйста, поговорите об этом специально в одноядерных процессорах.
Преимущество: нет блокировок, следовательно, нет тупиков и, как правило, лучшая масштабируемость
Недостаток: риск голодания (если алгоритм также не требует ожидания, но обычно это не так)
редактировать: алгоритмы без ожидания делают некоторые операции, когда он теряет CAS race.instead of busytry / startvation.
Пишите цикл повторных попыток CAS в своем источнике только в том случае, если нет встроенного языка, реализующего атомарную операцию, которую вы хотите. Аппаратное обеспечение (особенно x86) часто может работать лучше.
в Java AtomicInteger
имеет getAndIncrement()
а также incrementAndGet()
метод (начиная с Java 7 по крайней мере), что позволяет JVM легко преобразовать его в asm, что более эффективно, чем реальный цикл повторных попыток CAS. Это как в C ++ 11 std::atomic::fetch_add()
, Смотрите также Практическое использование для AtomicInteger.
На x86 вы хотите, чтобы ваша JVM использовала аппаратную поддержку x86 для этой операции. Это гораздо более вероятно, произойдет, если вы используете функцию, которая сопоставляет непосредственно к нему, вместо цикла CAS-повторов, что оптимизатор должен работать, чтобы оптимизировать в не-цикл реализации.
(Есть аппаратный арбитраж шины / кеша для lock
операции ed, когда несколько ядер ЦП конкурируют за одну и ту же строку кэша; только один поток одновременно может владеть строкой кэша и делать приращение. Можно утверждать, что это выжидательную бесплатно, даже если «шаги» — это тактовые циклы, а не инструкции процессора: вероятно, существует нижняя граница того, как долго lock
Операция ed может занять некоторое время в любой заданной системе, даже если все остальные ядра работают в одной строке кэша.)
; possible x86 implementation of incrementAndGet() for a 32-bit integer
; which you'd hopefully get (after inlining and so on)
mov eax,1
lock xadd [mem], eax ; atomically do [mem]+=eax, and put the old value in eax
inc eax ; old_value += 1 to get the new value
; result in EAX
Цикл не требуется.
На компьютере LL / SC (большинство не x86, например ARM, PowerPC, MIPS) будет цикл повторной попытки, но это не совсем CAS. И цикл повторных попыток CAS на машине LL / SC имеет дополнительные издержки. Это очень незначительно, но определенно лучше позволить JVM увидеть атомарную операцию, которую вы хотите напрямую. Увидеть Очистка атомарного младшего ненулевого бита от целого числа без знака для дальнейшего обсуждения CAS против LL / SC. Цикл CAS мог в теории оптимизировать в чистую петлю LL / SC.
Этот вопрос также является примером случая, когда ваша лучшая ставка (в источниках C ++ или Java) — это цикл повторных попыток CAS, поскольку в языке нет атомарного примитива, который делает то, что вы хотите. (Как и любое другое аппаратное обеспечение).