在 x86 CPU 上进行比较和交换

compare and exchange on x86 cpu

本文关键字:比较 交换 x86 CPU      更新时间:2023-10-16

例如,在英特尔 i3、i5、i7 x86 64 位 CPU 上,CAS 是否只保证最大 8 字节对象大小的原子?

在 x86 CPU 上,锁定指令被添加到 CAS 操作中,例如。 CMPXCHG,这意味着整个缓存行都被锁定为读取CPU,那么std::atomic::compare_exchange_weak()不会因为虚假故障原因返回故障?

如果 x86 CPU 在 CAS 操作中使用lock,那么如果在共享资源上使用无锁编程而不是使用 std::mutex,性能会提高什么?

例如,如果我想写一个无锁链接列表。我在标头节点的指针上进行原子加载并将其与std::atomic::compare_exchange_weak()进行比较以查看已进行的任何更改。在这种情况下,ABA 问题是否适用于 x86 CPU?

你在这里有几个问题:-)

  1. 根据定义,CAS 操作始终是原子操作。话虽如此,支持哪些 CAS 操作(如果有)取决于您的硬件,包括可以交换用于此类操作的最大字节数。某些 CPU(例如 ARM)根本不直接支持 CAS。x86-64 CPU 支持 8 字节 CAS,所有现代 CPU 也通过 CMPXCHG16b 指令支持 16 字节 CAS(通常称为双宽 CAS)。

  2. 我不确定 CAS 是否会在您提到的 CPU 上虚假失败(尽管我知道在某些平台上它不会)。我对这些特定 CPU 上的缓存架构了解不够。但是,在compare_exchange_weakcompare_exchange_strong之间进行选择时,底层硬件实现是无关紧要的:您应该使用对您正在做的事情有意义的一个 - 如果您只是编写一个典型的小 CAS 循环,则为弱交换(其中对虚假故障的额外工作可以忽略不计),否则则进行强交换。这也使您的代码更具可移植性和健壮性。

  3. 您需要测量。这几乎完全取决于您的应用程序正在做什么(如果您的应用程序确实受到围绕共享资源的极高争用的瓶颈,那么它可能会受益于首先需要较少共享的重新设计)。一般来说,std::mutex是"足够好的"(实际上大多数时候性能相当高),但如果在高争用下锁内有非常少量的工作,锁定开销确实可能会使实际工作量黯然失色,而无锁算法可以显着提高吞吐量。

  4. ABA 绝对适用于 x86。这是一个由于 CAS 本身的基本性质而引起的问题,无论硬件实现如何。我在这里写了一些关于它的文章。

我的建议是在编写无锁代码时要非常小心。它非常难以测试,并且可能会工作多年(即使在生产中),然后在某些极端情况下(例如,在稍微不同的硬件上,或者在不同的工作负载下使用时等)错误变得可见。不要开始编写像链表这样的通用数据结构,因为在一般情况下正确处理插入和删除是一场噩梦(至少,如果您的目标是在争用下保持高性能,通常是这样,因为这就是您最终编写无锁数据结构的原因)。相反,请找出特定应用程序逻辑所需的确切最小操作,并仅实现这些操作。只添加无锁链表编写起来相当简单;由于ABA,结合去除头部或尾部的能力要复杂得多。