另一周,我写了一个小线程类和一个单向消息管道,以允许线程之间的通信(每个线程两个管道,显然,用于双向通信).在我的Athlon 64 X2上一切正常,但我想知道如果两个线程都在查看相同的变量并且每个核心上的此变量的本地缓存值不同步,我是否会遇到任何问题.
我知道volatile关键字会强制变量从内存中刷新,但多核x86处理器是否有办法强制所有内核的缓存同步?这是我需要担心的事情,还是易失性和正确使用轻量级锁定机制(我使用_InterlockedExchange设置我的易失性管道变量)处理我想为多核x86 CPU编写"无锁"代码的所有情况?
我已经知道并使用了Critical Sections,Mutexes,Events等等.我主要想知道是否有x86内在函数,我不知道哪种力量或可用于强制缓存一致性.
volatile
只强制您的代码重新读取该值,它无法控制从中读取值的位置.如果你的代码最近读取了这个值,那么它可能会在缓存中,在这种情况下,volatile会强制它从缓存中重新读取,而不是从内存中读取.
x86中没有很多缓存一致性指令.有预取指令prefetchnta
,但不影响内存排序语义.它曾经通过将值带到L1缓存而不会污染L2来实现,但对于具有大型共享包含 L3缓存的现代英特尔设计而言,情况更为复杂.
x86 CPU的使用上的变化MESI协议(MESIF英特尔,MOESI为AMD),以保持它们的高速缓存相干互相(包括不同的内核的专用L1高速缓存).想要编写缓存行的核心必须强制其他核心使其副本无效,然后才能将其自己的副本从"共享"更改为"已修改"状态.
您不需要任何围栏指令(如MFENCE)在一个线程中生成数据并在x86上使用另一个线程,因为x86加载/存储具有内置的获取/释放语义.您确实需要MFENCE(全屏障)以获得顺序一致性.(此答案的先前版本表明clflush
需要,这是不正确的).
您确实需要阻止编译时重新排序,因为C++的内存模型是弱排序的. volatile
这是一个古老而糟糕的方式; C++ 11 std :: atomic是一种更好的编写无锁代码的方法.
由于x86处理器采用的MESI协议,核心之间保证了高速缓存一致性.在处理外部硬件时,您只需要担心内存一致性,外部硬件可能会在数据仍位于内核缓存上时访问内存.但是,这看起来不像你的情况,因为文本暗示你在用户区编程.
您不必担心缓存一致性.硬件将负责这一点.您可能需要担心的是由于缓存一致性导致的性能问题.
如果核心#1写入变量而核心#2读取相同的变量,则处理器将确保更新核心#2的高速缓存.由于必须从内存中读取整个高速缓存行(64字节),因此会产生一些性能成本.在这种情况下,这是不可避免的.这是期望的行为.
问题是,当您在同一缓存行中有多个变量时,即使内核正在同一缓存行中读取/写入不同的变量,处理器也可能会花费额外的时间来保持缓存同步.通过确保这些变量不在同一缓存行中,可以避免该成本.此效果称为False Sharing,因为您强制处理器同步线程之间实际不共享的对象的值.
挥发无法做到.在C++中,volatile仅影响编译器优化,例如将变量存储在寄存器而不是内存中,或者完全删除它.
您没有指定要使用的编译器,但如果您使用的是Windows,请在此处查看本文.另请参阅此处可用的ynchronization函数.您可能需要注意,通常volatile
不足以完成您希望它执行的操作,但在VC 2005和2008中,添加了非标准语义,这会在读取和写入之间添加隐含的内存屏障.
如果你想让事物变得便携,那么你将面临更艰难的道路.