C++ 中有没有办法确定 CPU 的缓存大小?我有一个处理大量数据的算法,我想将这些数据分解成块,以便它们适合缓存。这可能吗?您能否给我任何其他有关考虑缓存大小的编程提示(尤其是在多线程/多核数据处理方面)?
谢谢!
C++ 中有没有办法确定 CPU 的缓存大小?我有一个处理大量数据的算法,我想将这些数据分解成块,以便它们适合缓存。这可能吗?您能否给我任何其他有关考虑缓存大小的编程提示(尤其是在多线程/多核数据处理方面)?
谢谢!
根据 Ulrich Drepper 的“每个程序员都应该了解的内存”,您可以在 Linux 上执行以下操作:
一旦我们有了内存需求的公式,我们就可以将它与缓存大小进行比较。如前所述,缓存可能与多个其他内核共享。目前{将来肯定会有更好的方法!}在没有硬编码知识的情况下获得正确信息的唯一方法是通过 /sys 文件系统。在表 5.2 中,我们看到了内核发布的关于硬件的内容。程序必须找到目录:
/sys/devices/system/cpu/cpu*/cache
这在第 6 节:程序员可以做什么中列出。
他还在图 6.5 下描述了一个简短的测试,如果您无法从操作系统中获取 L1D 缓存大小,可以使用该测试来确定它。
我在他的论文中还遇到了另一件事: sysconf(_SC_LEVEL2_CACHE_SIZE)
是 Linux 上的一个系统调用,它应该返回 L2 缓存大小,尽管它似乎没有很好的文档记录。
C++ 本身并不“关心” CPU 缓存,因此不支持查询语言中内置的缓存大小。如果您正在为 Windows 开发,那么有GetLogicalProcessorInformation() 函数,可用于查询有关 CPU 缓存的信息。
预分配一个大数组。然后依次访问每个元素并记录每次访问的时间。理想情况下,当发生缓存未命中时,访问时间会发生跳跃。然后你可以计算你的 L1 缓存。它可能行不通,但值得一试。
你可以看到这个线程: http: //software.intel.com/en-us/forums/topic/296674
简短的答案在另一个线程中:
在现代 IA-32 硬件上,高速缓存行大小为 64。值 128 是 Intel Netburst 微架构(例如 Intel Pentium D)的遗产,其中 64 字节行配对成 128 字节扇区。当一个扇区中的一条线被取出时,硬件也会自动读取该扇区中的另一条线。因此,从错误共享的角度来看,Netburst 处理器上的有效行大小为 128 字节。(http://software.intel.com/en-us/forums/topic/292721)
读取 cpu (x86) 的 cpuid,然后通过查找表确定缓存大小。该表必须填写 cpu 制造商在其编程手册中发布的缓存大小。
根据您要执行的操作,您也可以将其留给某个图书馆。既然您提到了多核处理,您可能想看看Intel Threading Building Blocks。
TBB 包括缓存感知内存分配器。更具体地说,检查cache_aligned_allocator
(在参考文档中,我找不到任何直接链接)。
有趣的是,我不久前编写了一个程序来执行此操作(虽然在 C 中,但我确信它很容易合并到 C++ 代码中)。
http://github.com/wowus/CacheLineDetection/blob/master/Cache%20Line%20Detection/cache.c
get_cache_line 函数是一个有趣的函数,它返回数组访问时序数据中最大峰值之前的位置。它在我的机器上猜对了!如果有的话,它可以帮助您自己制作。
它基于这篇文章,最初激起了我的兴趣:http: //igoro.com/archive/gallery-of-processor-cache-effects/
IIRC,GCC 有一个__builtin_prefetch
提示。
http://gcc.gnu.org/onlinedocs/gcc-3.3.6/gcc/Other-Builtins.html
对此有一个很好的部分。基本上,它建议:
__builtin_prefetch (&array[i + LookAhead], rw, locality);
其中rw是 0(准备读取)或 1(准备写入)值,locality使用数字 0-3,其中 0 表示没有局部性,3 是非常强的局部性。
两者都是可选的。LookAhead 将是要向前看的元素的数量。如果内存访问为 100 个周期,并且展开的循环相隔两个周期,则 LookAhead 可以设置为 50 或 51。
有两种情况需要区分。您需要在编译时或运行时知道缓存大小吗?
对于某些应用程序,您知道代码将在其上运行的确切架构,例如,您是否可以直接在主机上编译代码。在这种情况下,简化查找大小并对其进行硬编码是一种选择(可以在构建系统中自动化)。在当今的大多数机器上,L1 高速缓存行应该是 64 字节。
如果您想避免这种复杂性,或者如果您需要支持在未知架构上编译,您可以使用 C++17 特性 std::hardware_constructive_interference_size作为一个很好的后备。它将为缓存行提供编译时估计,但要注意它的局限性。请注意,编译器在创建二进制文件时无法完美猜测,因为缓存线的大小通常取决于体系结构。
在运行时,您的优势在于您知道确切的机器,但您需要特定于平台的代码才能从操作系统读取信息。一个很好的起点是这个答案的代码片段,它支持主要平台(Windows、Linux、MacOS)。以类似的方式,您还可以在运行时读取 L2 缓存大小。
我建议不要尝试通过在启动时运行基准测试并测量哪个性能最好来猜测缓存行。它可能很好用,但如果 CPU 被其他进程使用,它也容易出错。
如果您必须发布一个二进制文件,并且稍后将在其上运行的机器具有一系列具有不同缓存大小的不同架构,您可以为每个缓存大小创建专门的代码部分,然后动态(在应用程序启动时)选择最合适的一。
缓存通常会做正确的事情。普通程序员唯一真正担心的是错误共享,您无法在运行时处理它,因为它需要编译器指令。