【问题标题】:False sharing and 128-byte alignment/padding虚假共享和 128 字节对齐/填充
【发布时间】:2015-05-25 19:48:01
【问题描述】:

在研究无锁/无等待算法时,我偶然发现了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 字节,实际上,到目前为止,我在这件事上找到的所有资源,包括来自 Intel 的 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 操作如何干扰在大量使用原子操作的情况下有人能告诉我这怎么可能发生吗?

【问题讨论】:

  • 这里的一个重要概念是“缓存关联性”
  • 英特尔优化手册,第 2.1.5.4 章。空间预取器努力将缓存行对保留在 L2 缓存中。
  • @Hans:我会接受这个答案(虽然在第 2.2.5.4 章中找到)。
  • 你的可能是最新的。你可以自己写答案。
  • @Ben:有用的提示。谢谢!

标签: c++ locking false-sharing


【解决方案1】:

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

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

【讨论】:

【解决方案2】:

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

http://goo.gl/8L6cUl

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

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

http://goo.gl/iNAZlX

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

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

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

【讨论】:

  • 不过,pair of cache line also see interference ... observed for atomic increment on Sandy Bridge ... 看来他们确实更新了 FOLLY_ALIGN_TO_AVOID_FALSE_SHARING 值,因为对 那个 特定架构进行了一系列测试。即:根据这些结果,64 字节对齐和填充是不够的。
【解决方案3】:

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

因此,从性能的角度来看,如果您有一个线程使用的变量,请通过对齐数据到那个值——这意味着每个数据块都从一个均匀的缓存线边界开始,没有其他处理器会“共享”相同的数据(除非你真的在线程之间共享数据——此时你必须做相关的事情缓存维护以确保数据在处理器之间正确更新)

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

【讨论】:

  • 我理解虚假分享的概念。我的问题是关于他们用来进行对齐和填充的值(128 字节)与 强制 他们选择这个值的架构(即:Sandy Bridge,它有 64 字节的缓存线 AFAIK) .
  • 我认为一些较旧的英特尔架构也使用 128 字节缓存线。也许他们在过去几年中还没有生产处理器?
  • 您已经做出了详尽的回答,对此我表示感谢。但是请阅读我公认的太长且可能不清楚的问题:Microbenchmarks indicate that pairs of cache lines also see interference ... (observed ... on Sandy Bridge). 这里的架构没有太多值得怀疑的空间。由于 Sandy Bridge 的结果不佳,他们将对齐方式更改为 128 字节。我想了解的是,在这种特殊情况下,更大的填充为什么以及如何减少错误共享。 @Hans 对这个问题的评论可能会让你感兴趣,就像我一样。
  • 不幸的是,我对最新的英特尔处理器并不是特别熟悉。大约 12 到 15 年前,当我在 AMD 进行基准测试时,我非常了解模型之间的差异。这些天没那么多了,因为我的家用机器都有 AMD 处理器(仍然忠于我大约 10 年前工作的品牌)并且在过去 8 年左右的时间里只使用 ARM 处理器。 [嗯,我的工作机器本身有一个 Intel 处理器的一些变体或其他,但我并没有真正关注它的行为或行为方式]
猜你喜欢
  • 2016-09-27
  • 2012-01-10
  • 1970-01-01
  • 2015-07-28
  • 2019-03-07
  • 2014-03-14
  • 1970-01-01
  • 1970-01-01
  • 2017-12-09
相关资源
最近更新 更多