比较并交换不适用于许多核心

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

【讨论】:

以上是关于比较并交换不适用于许多核心的主要内容,如果未能解决你的问题,请参考以下文章

为啥比较选中的属性不适用于检查至少一个复选框被选中?

字符串比较似乎不适用于从文件中读取的行

v-bind:style 指令不适用于条件比较

数据结构 - 选择排序

一分钟算法——选择排序

比较和交换如何用于任何共享数据结构的无等待互斥?