虚假共享和128字节对齐/填充

时间:2015-03-22 20:59:08

标签: c++ locking false-sharing

在做一些关于无锁/无等待算法的研究时,我偶然发现了false sharing问题。挖掘更多的东西让我得到了Folly的源代码(Facebook的C ++库),更具体地说是这个header file以及FOLLY_ALIGN_TO_AVOID_FALSE_SHARING宏的定义(目前在第130行)。乍一看最令我惊讶的是价值: 128 (即:而不是64)......

/// An attribute that will cause a variable or field to be aligned so that
/// it doesn't have false sharing with anything at a smaller memory address.
#define FOLLY_ALIGN_TO_AVOID_FALSE_SHARING __attribute__((__aligned__(128)))

AFAIK,现代CPU上的缓存块长度为64字节,实际上,到目前为止,我发现的每一项资源,包括英特尔的this article,都谈到64字节对齐填充帮助解决虚假分享问题。

然而,Facebook的人们在需要时将他们的班级成员对齐并填充到128个字节。然后我在FOLLY_ALIGN_TO_AVOID_FALSE_SHARING的定义上面找到了解释的开头:

enum {
    /// Memory locations on the same cache line are subject to false
    /// sharing, which is very bad for performance.  Microbenchmarks
    /// indicate that pairs of cache lines also see interference under
    /// heavy use of atomic operations (observed for atomic increment on
    /// Sandy Bridge).  See FOLLY_ALIGN_TO_AVOID_FALSE_SHARING
    kFalseSharingRange = 128
};

虽然它给了我更多细节,但我仍然觉得我需要一些见解。我很好奇连续缓存行的同步,或者它们上的任何RMW操作如何在大量使用原子操作时干扰 >。 有人可以告诉我这是怎么发生的吗?

3 个答案:

答案 0 :(得分:1)

正如汉斯在评论中指出的那样,有关此问题的一些信息可以在"Intel® 64 and IA-32 architectures optimization reference manual"的第3.7.3节“用于二级缓存的硬件预取”中找到有关英特尔酷睿微体系结构的信息:

“ Streamer —将数据或指令从内存加载到第二级缓存。要使用Streamer,以128字节的块(以128字节对齐)组织数据或指令。到内存中此块中的两个高速缓存行之一时,将触发流媒体预取对行。”

答案 1 :(得分:-1)

无论您是否使用原子操作,缓存都有一个“缓存行”,这是缓存操作的最小单位。其范围为32到128个字节,具体取决于处理器型号。虚假共享是指同一缓存行中的元素在不同线程(在不同处理器上运行[1])之间“共享”的时间。当发生这种情况时,一个处理器更新“其值”,将强制所有其他处理器“摆脱其数据的副本”。在原子操作的情况下会变得更糟,因为执行任何原子操作,执行操作的处理器将需要确保所有其他处理器在更新值之前摆脱了“他们的副本”(以确保没有其他处理器正在使用在值更新之前的“旧”值) - 这需要通过系统传播大量缓存维护消息,并且处理器重新加载它们先前在缓存中的值。

因此,从性能角度来看,如果您有一个线程使用的变量,请将它们分离到它们自己的缓存行(在原始帖子的示例中,假设这是128个字节),方法是将它们对齐。数据到该值 - 意味着每个数据块都在偶数缓存行边界上开始,没有其他处理器将“共享”相同的数据(除非您真正在线程之间共享数据 - 此时您必须执行相关操作缓存维护以确保在处理器之间正确更新数据)

[1]或具有多个内核的现代CPU中的处理器内核。为简单起见,我使用术语“处理器”或“处理器”来对应一个插槽内的实际处理器插槽或处理器内核。对于这个讨论,区别几乎是无关紧要的。

答案 2 :(得分:-1)

看起来,虽然英特尔使用64字节缓存行,但还有各种其他架构使用128字节缓存行...例如:

http://goo.gl/8L6cUl

  

Power Systems使用128字节长度的缓存行。与英特尔处理器(64字节高速缓存行)相比,这些更大的高速缓存行具有......

我发现散布在互联网上的注意事项,其他架构,甚至旧架构都是这样做的:

http://goo.gl/iNAZlX

  

原始计算机中的SGI MIPS R10000处理器

     

处理器的缓存行大小为128字节。

因此,Facebook程序员可能希望安全地使用它,并且不希望基于处理器架构拥有大量#define / #if,并且存在一些较新的英特尔处理器的风险一个128字节的缓存行,没有人记住纠正代码。