【问题标题】:Aligning to cache line and knowing the cache line size与缓存行对齐并了解缓存行大小
【发布时间】:2011-11-09 01:11:13
【问题描述】:

为了防止错误共享,我想将数组的每个元素与缓存行对齐。所以首先我需要知道缓存行的大小,所以我为每个元素分配了相应的字节数。其次,我希望数组的开头与缓存行对齐。

我使用的是 Linux 和 8 核 x86 平台。首先我如何找到缓存线的大小。其次,如何与 C 中的缓存行对齐。我使用的是 gcc 编译器。

因此结构将如下所示,假设高速缓存行大小为 64。

element[0] occupies bytes 0-63
element[1] occupies bytes 64-127
element[2] occupies bytes 128-191

等等,当然假设 0-63 与缓存行对齐。

【问题讨论】:

  • 也许这会有所帮助:stackoverflow.com/questions/794632/…
  • 但它没有显示如何使用 gcc 对齐缓存。
  • 使用 64 字节的编译时常量作为高速缓存行大小并不是一个坏主意,因此编译器可以将其烘焙到关心它的函数中。让编译器为运行时可变的缓存行大小生成代码可能会消耗对齐事物的一些好处,特别是在自动矢量化的情况下,如果它知道指针与缓存行对齐,它可以帮助编译器生成更好的代码宽度(比 SIMD 向量宽度更宽)。

标签: c linux caching computer-architecture memory-alignment


【解决方案1】:

我使用的是 Linux 和 8 核 x86 平台。首先我如何找到缓存行大小。

$ getconf LEVEL1_DCACHE_LINESIZE
64

将值作为宏定义传递给编译器。

$ gcc -DLEVEL1_DCACHE_LINESIZE=`getconf LEVEL1_DCACHE_LINESIZE` ...

在运行时sysconf(_SC_LEVEL1_DCACHE_LINESIZE) 可用于获取 L1 缓存大小。

【讨论】:

  • 这些sysconf()s 是在哪里指定的? POSIX / IEEE 标准 1003.1-20xx ?
  • @BrianCain 我用的是 Linux,所以我只做了man sysconf。 Linux 并不完全兼容 POSIX,因此特定于 Linux 的文档通常更有用。有时它已经过时了,所以你只需egrep -nH -r /usr/include -e '\b_SC'
  • 如果是 Mac,请使用 sysctl hw.cachelinesize
  • 通常我宁愿硬编码 64 而不是调用sysconf 来获得编译时恒定的行大小要好得多。编译器甚至不知道它是 2 的幂,因此您必须手动执行诸如 offset = ptr & (linesize-1) 之类的余数或位扫描 + 右移来实现除法。您不能只在对性能敏感的代码中使用/
【解决方案2】:

要知道尺寸,您需要使用处理器的文档来查找它,afaik 没有编程方法可以做到这一点。然而,从好的方面来说,大多数高速缓存行都是基于英特尔标准的标准大小。在 x86 缓存行上是 64 字节,但是,为了防止错误共享,您需要遵循您所针对的处理器的指南(英特尔在其基于 netburst 的处理器上有一些特别说明),通常您需要为此对齐 64 字节(英特尔声明您还应该避免跨越 16 字节边界)。

要在 C 或 C++ 中执行此操作,您需要使用标准 aligned_alloc 函数或编译器特定说明符之一,例如 __attribute__((align(64)))__declspec(align(64))。要在结构中的成员之间填充以将它们拆分到不同的缓存行,您需要插入一个足够大的成员以将其与下一个 64 字节边界对齐

【讨论】:

  • @MetallicPriest: gcc g++都支持__attributes__
  • @MetallicPriest:mmap&VirtualAlloc分配页对齐内存,一般页粒度为64kb(windows下),由于64kb是64的幂,所以会正确对齐。跨度>
  • 您可以通过编程方式获取缓存行大小。检查here。此外,您不能概括为在 x86 上拥有 64 字节缓存行。这只适用于最近的。
  • C++11 增加了 alignas,这是一种可移植的指定对齐方式
  • @NoSenseEtAl alignas 官方只支持对齐到 std::max_align_t 类型的大小,这通常是 long double 的对齐要求,也就是 8 或 16 字节 - 不幸的是不是 64。参见例如stackoverflow.com/questions/49373287/…
【解决方案3】:

另一种简单的方法是只 cat /proc/cpuinfo:

grep cache_alignment /proc/cpuinfo

【讨论】:

  • 也许你想删除一个无用的 cat。
【解决方案4】:

没有完全可移植的方法来获取缓存行大小。但是,如果您使用的是 x86/64,则可以调用 cpuid 指令来获取您需要了解的有关缓存的所有信息 - 包括大小、缓存行大小、级别数等...

http://softpixel.com/~cwright/programming/simd/cpuid.php

(向下滚动一点,该页面是关于 SIMD 的,但它有一个部分获取缓存线。)

至于对齐数据结构,也没有完全可移植的方法。 GCC 和 VS10 有不同的方法来指定结构的对齐方式。 “破解”它的一种方法是用未使用的变量填充您的结构,直到它与您想要的对齐方式匹配。

为了对齐你的 malloc(),所有主流编译器也为此目的对齐了 malloc 函数。

【讨论】:

    【解决方案5】:

    posix_memalign or valloc 可用于将分配的内存与缓存行对齐。

    【讨论】:

    • 我知道这是你自己的问题,但对于未来的读者,你可以回答这两个部分:-)
    • Steve,你知道 mmap 映射的内存是否与缓存行对齐。
    • 我不认为 Posix 能保证这一点,但如果 linux 总是选择页面对齐的地址,我也不会感到惊讶,更不用说缓存行对齐了。 Posix 表示,如果调用者指定第一个参数(地址提示),则必须是页面对齐的,并且映射本身始终是整数页数。这是强烈的暗示,实际上并没有保证任何事情。
    • 是的,mmap 仅在页面方面起作用,并且页面总是大于缓存行。即使在一些理论上的怪异架构中,缓存行不会大于页面也有很好的理由(缓存通常是物理标记的,因此不能将一条行拆分为 2 个虚拟页面而不会给 CPU 设计者带来极大的痛苦)。
    【解决方案6】:

    Here's a table 我制作的上面有大多数 Arm/Intel 处理器。您可以在定义常量时将其用作参考,这样您就不必为所有架构概括缓存行大小。

    对于 C++,希望我们很快就会看到hardware interface size,这应该是获取此信息的准确方法(假设您告诉编译器您的目标架构)。

    【讨论】:

    • 编译器不愿意实现hardware_destructive_interference_size,因为你真的希望它是一个编译时间常数,但如果你正在编译一个“通用”目标,它就不可能总是这样在同一 ISA 的多个 CPU 上运行。保守的选择是可能的,但不能保证不会过时。 (例如 128 字节,以说明当前具有 64 字节行的 x86 CPU 和喜欢完成对齐的行对的 L2 空间预取。(主流 Intel))
    【解决方案7】:

    如果有人对如何在 C++ 中轻松地做到这一点感到好奇,我已经构建了一个带有 CacheAligned<T> 类的库,该类处理确定缓存行大小以及 T 对象的对齐方式,通过调用引用.Ref() 在您的 CacheAligned<T> 对象上。如果您事先知道缓存行大小,也可以使用Aligned<typename T, size_t Alignment>,或者只是想坚持使用非常常见的值 64(字节)。

    https://github.com/NickStrupat/Aligned

    【讨论】:

    • @James - alignas 是 C++11。它不适用于 C++03。而且它不适用于许多 Apple 平台。在他们的一些操作系统上,Apple 提供了古老的 C++ 标准库,它们伪装成 C++11,但缺少 unique_ptralignas 等。
    • @James 同样,该标准仅要求 alignas 支持最多 16 个字节,因此任何更高的值都不能移植。由于几乎所有现代处理器的缓存线大小都是 64 字节,alignas 没有用处,除非您知道您的编译器支持 alignas(64)
    • alignas 也在 C11 中,而不仅仅是 C++11。
    • alignas 官方仅支持对齐到 std::max_align_t 类型的大小,这通常是 long double 的对齐要求,也就是 8 或 16 字节 - 不幸的是不是 64。跨度>
    • @NickStrupat 似乎终于在 C++17 中添加了对缓存行大小对齐的支持。对于 C++17,我的最后一条评论似乎也不再正确(问题仅仅是 operator new 不能保证返回的内存对齐比 std::max_align_t 更好)。我刚刚发现这个:en.cppreference.com/w/cpp/thread/…
    猜你喜欢
    • 1970-01-01
    • 2012-01-18
    • 1970-01-01
    • 1970-01-01
    • 2017-10-19
    • 2013-07-05
    • 2021-12-02
    • 2015-02-07
    相关资源
    最近更新 更多