C ++缓存感知编程

时间:2009-12-17 14:46:59

标签: c++ optimization caching cpu-cache

在C ++中是否有办法确定CPU的缓存大小?我有一个处理大量数据的算法,我想将这些数据分解成块,以便它们适合缓存。这可能吗? 你能否给我一些关于编程的其他提示(特别是在多线程/多核数据处理方面)?

谢谢!

10 个答案:

答案 0 :(得分:15)

根据“What every programmer should know about memory”,Ulrich Drepper可以在Linux上执行以下操作:

  

一旦我们有了记忆的公式   我们可以将它与它进行比较   缓存大小。如前所述,   缓存可能与多个共享   其他核心。目前{有   肯定会很快成为一个   更好的方式!}唯一的方法来获得   没有硬编码的正确信息   知识是通过/ sys   文件系统。在表5.2中我们已经看到了   内核发布的内容   硬件。一个程序必须找到   目录:

/sys/devices/system/cpu/cpu*/cache

这列在Section 6: What Programmers Can Do

他还描述了图6.5右下方的一个简短测试,如果你无法从操作系统中获取它,可用于确定L1D缓存大小。

我在他的论文中还有另外一件事:sysconf(_SC_LEVEL2_CACHE_SIZE)是Linux上的一个系统调用,它应该返回L2缓存大小,尽管它似乎没有很好地记录。

答案 1 :(得分:11)

C ++本身并不“关心”CPU缓存,因此不支持查询语言中内置的缓存大小。如果您正在为Windows开发,那么可以使用GetLogicalProcessorInformation()-function,它可用于查询有关CPU缓存的信息。

答案 2 :(得分:8)

预分配大型数组。然后按顺序访问每个元素并记录每次访问的时间。理想情况下,当发生高速缓存未命中时,访问时间会有所增加。然后你可以计算你的L1缓存。它可能不起作用但值得尝试。

答案 3 :(得分:4)

读取cpu(x86)的cpuid,然后通过查找表确定缓存大小。该表必须填写cpu制造商在其编程手册中发布的缓存大小。

答案 4 :(得分:4)

根据您尝试做的事情,您可能还会将其留给某个图书馆。由于您提到了多核处理,因此您可能需要查看Intel Threading Building Blocks

TBB包括缓存感知内存分配器。更具体地说,检查cache_aligned_allocator(在参考文档中,我找不到任何直接链接)。

答案 5 :(得分:4)

有趣的是,我之前编写了一个程序来执行此操作(虽然在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/

答案 6 :(得分:4)

您可以看到此主题:http://software.intel.com/en-us/forums/topic/296674

简短的回答是在另一个主题中:

  

在现代IA-32硬件上,缓存行大小为64.值128为   英特尔Netburst微体系结构的遗产(例如Intel Pentium   D)其中64字节行与128字节扇区配对。当一条线   在获取扇区中,硬件自动获取另一个扇区   该行业也是如此。所以从虚假分享的角度来看,   Netburst处理器上的有效行大小为128字节。 (http://software.intel.com/en-us/forums/topic/292721

答案 7 :(得分:1)

IIRC,海湾合作委员会有__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,其中零为否地方,3是非常强大的地方。

两者都是可选的。 LookAhead是要展望的元素数量。如果内存访问是100个周期,并且展开的循环相隔两个周期,LookAhead可以设置为50或51。

答案 8 :(得分:0)

有两种情况需要区分。您是否需要在编译时或运行时知道缓存大小?

确定编译时的缓存大小

对于某些应用程序,您知道代码将在其上运行的确切体系结构,例如,如果可以直接在主机上编译代码。在这种情况下,简化查找大小并进行硬编码是一个选择(可以在构建系统中自动进行)。在当今的大多数计算机上,L1缓存行应为64字节。

如果要避免这种复杂性,或者需要在未知体系结构上支持编译,则可以使用C ++ 17功能std::hardware_constructive_interference_size作为良好的后备。它将为缓存行提供编译时估计,但是为be aware of its limitations。请注意,编译器在创建二进制文件时无法完全猜测,因为缓存行的大小通常取决于体系结构。

确定运行时的缓存大小

在运行时,您具有知道确切机器的优势,但是需要特定于平台的代码才能从OS读取信息。一个很好的起点是this answer的代码片段,它支持主要平台(Windows,Linux,MacOS)。您也可以用类似的方式在运行时读取L2缓存大小。

我建议不要通过在启动时运行基准测试并评估性能最好的基准来猜测高速缓存行。这可能很好用,但如果CPU被其他进程使用,则也容易出错。

结合两种方法

如果必须运送一个二进制文件,并且稍后将在该二进制文件上运行的计算机具有一系列具有不同缓存大小的不同体系结构,则可以为每种缓存大小创建专门的代码部分,然后动态地(在应用程序启动时)选择最合适的一个。

答案 9 :(得分:-1)

缓存通常会做正确的事情。普通程序员唯一真正的担心是错误共享,你不能在运行时处理它,因为它需要编译器指令。