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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了虚假共享和128字节对齐/填充相关的知识,希望对你有一定的参考价值。

在做一些关于无锁/无等待算法的研究时,我偶然发现了false sharing问题。挖掘更多的东西让我得到了Folly的源代码(Facebook的C ++库),更具体地说是这个header fileFOLLY_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操作如何相互干扰。有人可以告诉我这甚至可能发生的事情吗?

答案

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

“Streamer - 将数据或指令从内存加载到二级缓存。要使用流式传输器,请以128字节为单位组织数据或指令,以128字节对齐。首先访问此块中两条缓存行之一当它在内存中时触发流式传输器预取对线。“

另一答案

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

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

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

另一答案

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

http://goo.gl/8L6cUl

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

我发现在互联网上注意到其他架构,即使是旧架构也是这样做的:

http://goo.gl/iNAZlX

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

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

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

以上是关于虚假共享和128字节对齐/填充的主要内容,如果未能解决你的问题,请参考以下文章

内存对齐:如何使用 alignof / alignas?

GCC - 如何重新对齐堆栈?

SSE 向量重新对齐?

并行框架,避免虚假共享

C++ 结构体对齐

内存对齐:C/C++编程中的重要性和技巧