1

谁能给我总结一下比较和交换编程的优缺点?(例如多核 CPU 性能)

这是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;
    }
}

=== 编辑===

请在单核/核心 CPU 中专门讨论这个问题。

4

2 回答 2

4

优点:没有锁,因此没有死锁,并且通常具有更好的可扩展性

缺点:饥饿的风险(除非算法也是免等待的,但一般情况并非如此)

编辑:无等待算法在失去 CAS 比赛时会执行一些操作。而不是忙于尝试/启动。

于 2013-06-03T14:57:44.177 回答
0

如果没有实现您想要的原子操作的内置语言,请仅在您的源代码中编写 CAS 重试循环。 硬件(尤其是 x86)通常可以做得更好。

Java 的AtomicIntegerhasgetAndIncrement()incrementAndGet()方法(至少从 Java 7 开始)使得 JVM 可以轻松地将其 JIT 到 asm 中,这比实际的 CAS 重试循环更有效。这就像 C++11 的std::atomic::fetch_add(). 另请参阅AtomicInteger 的实际用途

在 x86 上,您希望您的 JVM 能够利用 x86 对这个操作的硬件支持。 如果您使用直接映射到它的函数,而不是优化器必须努力优化为非循环实现的 CAS 重试循环,则更有可能发生这种情况。

(当多个 CPU 内核竞争同一个缓存行时,ed 操作存在硬件总线/缓存仲裁lock;一次只有一个线程可以实际拥有缓存行并进行增量。您可以争辩说它是无等待的,即使“步骤”是时钟周期而不是 CPU 指令: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。LL/SC 机器上的 CAS 重试循环有额外的开销。很轻微,但是让JVM直接看到你想要的原子操作肯定更好。有关CAS 与 LL/SC 的更多讨论,请参阅原子清除无符号整数的最低非零位。CAS 循环理论上可以优化为纯 LL/SC 循环。

该问题也是您最好的选择(在 C++ 或 Java 源代码中)是 CAS 重试循环的示例,因为该语言没有执行您想要的操作的原子原语。(也没有任何常见的硬件)。

于 2018-08-16T07:35:57.863 回答