编译器优化对使用 PAPI 的 FLOP 和 L2/L3 缓存未命中率的影响

标签 c intel compiler-optimization cpu-cache flops

因此,我们的任务是编译一些代码(我们应该将其视为黑盒),使用不同的英特尔编译器优化标志(-O1 和 -O3)以及矢量化标志(- xhost 和 -no-vec) 并观察以下方面的变化:

  • 执行时间
  • 浮点运算 (FPO)
  • L2 和 L3 缓存未命中率

执行这些优化后,我们注意到执行时间有所缩短,这是意料之中的事情,考虑到编译器为提高效率而对您的代码所做的所有更改。然而,我们也注意到 FPO 的数量有所下降,虽然我们知道这是一件好事,但我们不确定为什么会这样。此外,我们注意到(并且无法解释)L2 缓存未命中率增加(随着优化级别的增加而增加),但缓存访问没有显着增加,L3 级别几乎没有变化。

完全不使用矢量化或优化在 L2 缓存未命中率方面产生了最佳结果,我们想知道你们是否可以给我们一些见解,以及我们可以使用的支持文档、文献和资源加深我们对这个主题的了解。

谢谢。

编辑:使用的编译器选项是:

  • O0 -no-vec(最高执行时间,最低 L2 缓存未命中)
  • O1(执行时间更短,L2 缓存未命中率更高)
  • O3(执行时间更少,L2 缓存未命中率更高)
  • xhost(与 -O3 执行时间相同,最高 L2 缓存未命中数)

更新:

虽然总体 L2 缓存访问略有下降,但实际未命中率却大幅增加。

使用 -0O -no-vec

以 usecs 为单位的挂钟时间:13,957,075

  • L2 缓存未命中:207,460,564
  • 二级缓存访问:1,476,540,355
  • 二级缓存未命中率:0.140504
  • L3 缓存未命中:24,841,999
  • L3 缓存访问:207,460,564
  • L3 缓存未命中率:0.119743

使用-xhost

以 usecs 为单位的挂钟时间:4,465,243

  • L2 缓存未命中:547,305,377
  • 二级缓存访问:1,051,949,467
  • 二级缓存未命中率:0.520277
  • L3 缓存未命中:86,919,153
  • L3 缓存访问:547,305,377
  • L3 缓存未命中率:0.158813

最佳答案

EOF 的回答对较少的浮点操作有很好的解释:-ffast-math 操作组合,所以我只回答另一部分。


这个问题没有关于使用什么 CPU 微架构的信息,但至少它被标记为 .

在 Intel CPU 上,有一些逻辑可以预取到 L1,还有更复杂的逻辑可以预取到 L2(从 L3 或主内存)。每个内核都有自己的 L2,但缓存层次结构的较低级别是共享的,因此它是放置主要预取逻辑的明显位置。

如果您的读取速度低于内存带宽的限制,您的负载将在 L2 中命中,因为硬件预取器已经将这些行提取到 L2 中。 如果预取跟不上,您将导致 L2 缓存未命中

更少的更宽负载而不是更多的标量负载也意味着 vector 的未命中百分比会更糟。 (EOF 的回答已经说明了这一点)。这种影响并不能解释 L2 未命中绝对数量的增加,但只有(部分)未命中百分比发生变化。不过,在查看数据时仍需牢记这一点。


来自 Intel 的优化指南( 标签 wiki 中的链接),第 2.3.5.4 节:数据预取:

Data Prefetch to the L2 and Last Level Cache

Streamer: This prefetcher monitors read requests from the L1 cache for ascending and descending sequences of addresses.... When a forward or backward stream of requests is detected, the anticipated cache lines are prefetched. Prefetched cache lines must be in the same 4K page.

  • The streamer may issue two prefetch requests on every L2 lookup. The streamer can run up to 20 lines ahead of the load request.
  • Adjusts dynamically to the number of outstanding requests per core. If there are not many outstanding requests, the streamer prefetches further ahead. If there are many outstanding requests it prefetches to the LLC only and less far ahead.
  • When cache lines are far ahead, it prefetches to the last level cache only and not to the L2. This method avoids replacement of useful cache lines in the L2 cache.
  • Detects and maintains up to 32 streams of data accesses. For each 4K byte page, you can maintain one forward and one backward stream can be maintained.

这是来自 Sandybridge 部分,但 Haswell 和 Skylake 部分没有详细介绍预取的更改。他们说“改进的预取”,但大概是相同的基本设计,只是具有更好的启发式算法和/或对现有启发式算法进行更好的调整,诸如此类。


感谢@HansPassant:他对这个问题的评论让我想到预取跟不上。

关于编译器优化对使用 PAPI 的 FLOP 和 L2/L3 缓存未命中率的影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26638572/

相关文章:

c - 使用 int 类型指针取消引用 char 值时获取垃圾

c - 函数中的变量是否可以保存 C 中函数旧调用的旧值?

c++ - 如何将两个 SSE 寄存器加在一起

optimization - 加载和转置八个 8 元素浮点向量

c - 包含来自静态库的头文件

c - scanf() 将换行符保留在缓冲区中

c# - Windows 上的 Intel Math Kernel,从 c# 调用以生成随机数

c++ - C++11 编译器在代码优化期间是否可以将局部变量转换为右值?

c - 如果启用优化器,GCC 浮点错误

c - gcc -O 优化不检测未使用变量的堆溢出