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

kfk*_*ili 6 c intel compiler-optimization flops cpu-cache

所以我们一直负责编译一些代码(我们应该把它当作黑盒子),使用不同的intel编译器优化标志(-O1和-O3)以及矢量化标志(-xhost和 - no-vec)并观察以下变化:

  • 执行时间处理时间
  • 浮点运算(FPOs)
  • L2和L3缓存丢失率

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

完全不使用矢量化或优化就L2缓存丢失率产生了最好的结果,我们想知道你们是否可以给我们一些见解,以及我们可以用来加深知识的支持文档,文献和资源关于这个话题.

谢谢.

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

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

更新:

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

使用-0O -no-vec

使用中的挂钟时间:13,957,075

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

使用-xhost

挂钟时间在usecs:4,465,243

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

Pet*_*des 2

EOF 的答案对更少的浮点操作有一个很好的解释:-ffast-math操作的样式组合,所以我只回答其他部分。


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

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

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

更少的更宽的负载而不是更多的标量负载也意味着向量的缺失百分比会更差。(EOF的回答已经表明了这一点)。不过,这种效应并不能解释 L2 未命中绝对数量的增加,只能解释(部分)未命中百分比的变化。不过,在查看数据时仍然需要牢记这一点。


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

数据预取至二级缓存和末级缓存

Streamer:此预取器监视来自 L1 缓存的读取请求,以获取地址的升序和降序序列。...当检测到向前或向后的请求流时,将预取预期的缓存行。预取的缓存行必须位于同一 4K 页中。

  • 流传输器可以在每次 L2 查找时发出两个预取请求。流送器最多可以在加载请求之前运行 20 行。
  • 动态调整每个核心的未完成请求数。如果没有太多未完成的请求,则流送器会进一步提前预取。如果有许多未完成的请求,它只会预取到 LLC 并且不会提前。
  • 当缓存行远远领先时,它仅预取到最后一级缓存,而不预取到 L2。该方法避免了替换L2高速缓存中的有用高速缓存行。
  • 检测并维护多达 32 个数据访问流。对于每个4K字节页,可以维护一个前向流和一个后向流。

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


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