在 x86 CPU 上进行比较和交换
compare and exchange on x86 cpu
例如,在英特尔 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?
你在这里有几个问题:-)
-
根据定义,CAS 操作始终是原子操作。话虽如此,支持哪些 CAS 操作(如果有)取决于您的硬件,包括可以交换用于此类操作的最大字节数。某些 CPU(例如 ARM)根本不直接支持 CAS。x86-64 CPU 支持 8 字节 CAS,所有现代 CPU 也通过
CMPXCHG16b
指令支持 16 字节 CAS(通常称为双宽 CAS)。 -
我不确定 CAS 是否会在您提到的 CPU 上虚假失败(尽管我知道在某些平台上它不会)。我对这些特定 CPU 上的缓存架构了解不够。但是,在
compare_exchange_weak
和compare_exchange_strong
之间进行选择时,底层硬件实现是无关紧要的:您应该使用对您正在做的事情有意义的一个 - 如果您只是编写一个典型的小 CAS 循环,则为弱交换(其中对虚假故障的额外工作可以忽略不计),否则则进行强交换。这也使您的代码更具可移植性和健壮性。 -
您需要测量。这几乎完全取决于您的应用程序正在做什么(如果您的应用程序确实受到围绕共享资源的极高争用的瓶颈,那么它可能会受益于首先需要较少共享的重新设计)。一般来说,
std::mutex
是"足够好的"(实际上大多数时候性能相当高),但如果在高争用下锁内有非常少量的工作,锁定开销确实可能会使实际工作量黯然失色,而无锁算法可以显着提高吞吐量。 -
ABA 绝对适用于 x86。这是一个由于 CAS 本身的基本性质而引起的问题,无论硬件实现如何。我在这里写了一些关于它的文章。
我的建议是在编写无锁代码时要非常小心。它非常难以测试,并且可能会工作多年(即使在生产中),然后在某些极端情况下(例如,在稍微不同的硬件上,或者在不同的工作负载下使用时等)错误变得可见。不要开始编写像链表这样的通用数据结构,因为在一般情况下正确处理插入和删除是一场噩梦(至少,如果您的目标是在争用下保持高性能,通常是这样,因为这就是您最终编写无锁数据结构的原因)。相反,请找出特定应用程序逻辑所需的确切最小操作,并仅实现这些操作。只添加无锁链表编写起来相当简单;由于ABA,结合去除头部或尾部的能力要复杂得多。
- shell排序中的交换和比较
- 不正确的比较和交换计数器输出用于快速排序功能
- 原子值的部分比较和完全交换
- 跟踪选择排序中的交换和比较次数
- 如何在C 中实施简单的比较和交换共享计数器
- 为什么比较和交换操作同时存在免费函数和成员函数?
- 计数交换/比较合并排序算法的数字
- 通过比较数组中的相邻元素来计算交换次数
- 使用C++原子库,我应该使用什么样的内存顺序进行加载,然后进行比较交换
- 128位比较和交换固有
- std::atomic<int*>::load 应该做一个比较和交换循环吗?
- 互锁。比较交换<Int> 使用大于或小于而不是相等
- 如何将比较和交换用于任何共享数据结构的免等待互斥
- 你能用lambda比较器交换std::队列吗
- C++11比较和交换获取/释放语义
- 原子比较运算符(无交换)
- C++中比较和交换
- 比较和交换三个原子变量
- 如何在C++中获取快速排序的移动,交换和比较计数
- 比较交换原子操作vs加载链接/存储条件操作