比较并交换不适用于许多核心
Posted
技术标签:
【中文标题】比较并交换不适用于许多核心【英文标题】:Compare-And-Swap not working on many Cores 【发布时间】:2017-03-30 06:49:17 【问题描述】:当我发现“CAS”指令时,我记得我很清楚它可以用于运行在单个 CPU 上的线程,但我很惊讶它可以用于多个 CPU
昨天,我第一次有机会在我的一个开发项目上对其进行测试。我实现了它,它真的很好用;我所有的单元测试都是绿色的。完美。
但是今天,我在另一台机器上运行了我的单元测试,但它们现在失败了。不太完美
两台机器的主要区别在于第一台(单元测试为绿色的那台)是一台老旧的笔记本电脑,只有一个内核!第二个是更新的i7,功能更强大...
现在,在我的 i7 上,如果我强制我的单元测试在一个内核上运行,它们就会成功。我通过运行来做到这一点
taskset -c <cpu-id> my-unit-test
合法地,我原来的问题又回来了:CAS 是否适用于许多内核?好吧,根据我读到的,如果没有,我会感到惊讶......
那又怎样?我希望它来自我的代码中的错误。为了给你更多信息,我有一个带有关键部分的类。我添加了一个属性
bool m_isBeingModified;
它被初始化为false
。此外,在我的关键部分的开头,我运行了函数
inline void waitForClassBeingModified()
while (!__sync_bool_compare_and_swap(&m_isBeingModified, false, true))
/// I concider that I can to such a loop as my critical section is very light/short
最后,在我的关键部分结束时,我重置了我的布尔变量
m_isBeingModified = false;
我尝试将我的属性设置为volatile
,但它没有改变任何东西:我的单元测试仍然失败
最后信息:
gcc --version
gcc (Ubuntu 6.2.0-5ubuntu12) 6.2.0 20161005
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
感谢您的帮助
【问题讨论】:
您是将自己的互斥锁作为学习经验还是编写生产代码?如果是后者,那么您可能需要重新考虑使用库(如 pthread)或操作系统中提供的同步对象。 我的代码不适用于生产。更多的是为了通过__sync_bool_compare_and_swap了解CAS的用法的测试 【参考方案1】:还可以使用__sync_bool_compare_and_swap
来取消设置变量,而不仅仅是m_isBeingModified = false;
。另外,不要实现自己的互斥锁...
编译器和 CPU 都可以以意想不到的方式重新排序代码。 __sync
原语的标记方式是为了防止这种重新排序发生。因此,对于m_isBeingModified = false;
,编译器很可能会首先将变量设置为false
,然后才为您打算在关键区域内的任何内容生成代码。
【讨论】:
谢谢。我对重新排序感到非常惊讶,因为如果我将我的属性声明为volatile
,也会发生这种情况。但是,您似乎是对的:如果我还使用__sync_bool_compare_and_swap
取消设置我的变量,我的问题就消失了。
“另外,不要实现自己的互斥锁”是什么意思?您的意思是我应该使用“标准”pthread 互斥锁还是您有其他想法?
是的,我的意思是您应该只使用“标准”pthread 互斥锁。
volatile
仅对编译器有意义。 CPU 根本看不到它,因此仍然允许围绕这个存储重新排序内存访问。此外,volatile
在 C 标准中没有多大意义。我真的不知道它有什么帮助。【参考方案2】:
多亏了 Uli 的宝贵帮助,我想我现在有了所有可以回答我的问题的要素。
首先,在此之前我可能还不清楚,但我想要防止并发访问的功能非常简单。完成大约需要 80 个 cpu 周期 (TSC)。这就是为什么比起使用pthread_mutex
,我更喜欢实现我自己的基于一个 CAS 的“轻量”并发互斥锁。
我发现 this interesting page 解释了如何“暂时”禁用代码重新排序,这要归功于以下说明:
__asm__ __volatile__("":::"memory");
使用它,我确实提高了并发保护,当然我所有的测试仍然成功。
总结一下,下面的列表报告了我尝试过的不同解决方案对性能的影响:
原码(无保护):80 TSC左右 双 CAS(设置和取消设置变量):大约 105 TSC 基于互斥锁的解决方案:大约 120 TSC 单个 CAS + 禁用重新排序:大约 85 TSC【讨论】:
以上是关于比较并交换不适用于许多核心的主要内容,如果未能解决你的问题,请参考以下文章