27

在做一些关于无锁/无等待算法的研究时,我偶然发现了错误共享问题。再深入一点,我找到了 Folly 的源代码(Facebook 的 C++ 库),更具体地说,是这个头文件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 字节,实际上,到目前为止,我在这件事上找到的所有资源,包括来自 Intel 的这篇文章,都谈到了 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 操作如何相互干扰有人可以告诉我这怎么可能发生?

4

3 回答 3

5

正如 Hans 在评论中指出的那样,有关这方面的一些信息可以在“英特尔® 64 和 IA-32 架构优化参考手册”的第 3.7.3 节“二级缓存的硬件预取”中找到,关于英特尔酷睿微架构:

"Streamer — 将数据或指令从内存加载到二级缓存。要使用 Streamer,请将数据或指令组织成 128 字节的块,在 128 字节上对齐。第一次访问此块中的两个缓存线之一当它在内存中时会触发流媒体预取线对。”

于 2019-04-14T08:52:45.223 回答
0

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

http://goo.gl/8L6cUl

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

我发现分散在互联网上的其他架构,甚至是旧架构,都在做同样的事情:

http://goo.gl/iNAZlX

源计算机中的 SGI MIPS R10000 处理器

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

因此,Facebook 程序员可能希望安全行事,并且不想拥有大量基于处理器架构的#define/集合,因为一些较新的英特尔处理器具有 128 字节缓存线并且没有人记得更正代码的风险。#if

于 2015-03-23T06:17:16.203 回答
-1

无论您是否使用原子操作,缓存都有一个“缓存行”,它是缓存操作的最小单位。这范围从 32 到 128 字节,具体取决于处理器型号。错误共享是指同一高速缓存行中的元素在不同线程(在不同处理器上运行 [1])之间“共享”。发生这种情况时,一个处理器更新“其值”,将迫使所有其他处理器“摆脱其副本”该数据。在原子操作的情况下会变得更糟,因为要执行任何原子操作,执行操作的处理器需要确保所有其他处理器在更新值之前已经摆脱了“它们的副本”(以确保没有其他处理器正在使用一个“老”

因此,从性能的角度来看,如果您有一个线程使用的变量,则通过将数据与值 - 意味着每个数据块都从一个均匀的缓存线边界开始,并且没有其他处理器将“共享”相同的数据(除非您真正在线程之间共享数据 - 此时您必须进行相关的缓存维护以确保数据在处理器之间正确更新)

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

于 2015-03-22T21:23:42.843 回答