c++ - 更短的循环,相同的覆盖范围,为什么我在使用 Visual Studio 2013 的 C++ 中得到更多的末级缓存未命中?

标签 c++ performance visual-studio-2013 x86 profiling

我试图了解是什么导致了缓存未命中,以及它们最终会在我们的应用程序性能方面造成多少损失。但是对于我现在所做的测试,我很困惑。

假设我的 L3 缓存是 4MB,我的 LineSize 是 64 字节,我希望这个循环(循环 1):

int8_t aArr[SIZE_L3];
int i;
for ( i = 0; i < (SIZE_L3); ++i )
{
  ++aArr[i];
}

...和这个循环(循环 2):

int8_t aArr[SIZE_L3];
int i;
for ( i = 0; i < (SIZE_L3 / 64u); ++i )
{
  ++aArr[i * 64];
}

给出大致相同数量的末级缓存未命中,但不同数量的包含性末级缓存引用。

但是,Visual Studio 2013 分析器提供给我的数字令人不安。

使用循环 1:

  • 包含末级缓存引用:53,000
  • 最后一级缓存未命中:17,000

使用循环 2:

  • 包含末级缓存引用:69,000
  • 最后一级缓存未命中:35,000

我已经使用一个动态分配的数组对此进行了测试,并在具有更大的 L3 缓存 (8MB) 的 CPU 上进行了测试,我在结果中得到了类似的模式。

为什么我没有得到相同数量的缓存未命中,为什么我在更短的循环中有更多引用?

最佳答案

分别递增 int8_t aArr[SIZE_L3]; 的每个字节已经足够慢了,以至于硬件预取器可能在很多时候都能保持良好状态。乱序执行可以让大量的读-修改-写同时运行到不同的地址,但最好的情况仍然是每个存储时钟一个字节。 (存储端口 uops 的瓶颈,假设这是系统上的单线程测试,对内存带宽没有很多其他要求)。

英特尔 CPU have their main prefetch logic in L2 cache (如英特尔优化指南中所述;请参阅 标签 wiki)。因此,在内核发出负载之前成功将硬件预取到 L2 缓存中意味着 L3 缓存永远不会丢失。

John McCalpin's answer on this Intel forum thread确认 L2 硬件预取未被正常性能事件(如 MEM_LOAD_UOPS_RETIRED.LLC_MISS)计为 LLC 引用或未命中。显然,您可以查看 OFFCORE_RESPONSE 事件。

IvyBridge introduced next-page HW prefetch .之前的 Intel 微体系结构在预取时不会跨越页面边界,因此您仍然每 4k 就会丢失一次。如果操作系统没有机会主义地将您的内存放入 2MiB 的大页面中,TLB 可能会丢失。 (但是当您接近页面边界时,推测性页面遍历可能会避免那里的很多延迟,并且硬件肯定会进行推测性页面遍历)。


对于 64 字节的跨度,执行接触内存的速度比缓存/内存层次结构能够跟上的速度快得多。您在 L3/主内存上遇到瓶颈。乱序执行可以同时保持大约相同数量的读取/修改/写入操作,但相同的乱序窗口覆盖 64 倍的内存。


更详细地解释确切的数字

对于 L3 左右的数组大小,IvyBridge's adaptive replacement policy可能会产生重大影响。

在我们知道确切的 uarch 和测试的更多细节之前,我不能说。目前尚不清楚您是否只运行了该循环一次,或者您是否有一个外部重复循环并且那些未命中/引用数字是每次迭代的平均值。

如果它仅来自单次运行,那是一个微小的噪声样本。我假设它在某种程度上是可重复的,但令我惊讶的是,对于每字节版本,L3 引用计数如此之高。 4 * 1024^2/64 = 65536,因此您接触的大部分缓存行仍然有一个 L3 引用。

当然,如果您没有重复循环,并且这些计数包括代码在循环之外所做的所有事情,那么这些计数中的大部分可能来自您程序中的启动/清理开销。 (即你的循环被注释掉的程序可能有 48k L3 引用,IDK。)


I have tested this with a dynamically allocated array

完全不足为奇,因为它仍然是连续的。

and on a CPU that has a larger L3 cache (8MB) and I get a similar pattern in the results.

这个测试是否使用了更大的阵列?或者您是否在具有 8MiB L3 的 CPU 上使用了 4MiB 阵列?

关于c++ - 更短的循环,相同的覆盖范围,为什么我在使用 Visual Studio 2013 的 C++ 中得到更多的末级缓存未命中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38341426/

相关文章:

c++线程不执行

c++ - eof() 返回什么?

c++ - 在 main 方法中调用返回字符串的函数

c++ - 减少以下代码中的时间

visual-studio - 无法在 VS 2013 中启动 IIS Express Web 服务器

c++ - 特征矩阵初始化的 clang 格式

sql-server - 拆分表以每秒处理 100 次插入?

c++ - 哪个更快/更有效率?

c# - 如何在 Visual Studio 2013 中启用 C# 6.0 功能?

c++ - 我应该如何处理编写一段确定订阅包节省的代码的逻辑