【问题标题】:Linux perf tool showing weird cache miss resultsLinux perf 工具显示奇怪的缓存未命中结果
【发布时间】:2017-07-28 07:43:22
【问题描述】:

我正在使用 linux perf 工具来分析 one of CRONO benchmarks,我对 L1 DCache Misses 特别感兴趣,所以我这样运行程序:

perf record -e L1-dcache-read-misses -o perf/apsp.cycles apps/apsp/apsp 4 16384 16

它运行良好,但会产生这些警告:

WARNING: Kernel address maps (/proc/{kallsyms,modules}) are restricted,
check /proc/sys/kernel/kptr_restrict.

Samples in kernel functions may not be resolved if a suitable vmlinux
file is not found in the buildid cache or in the vmlinux path.

Samples in kernel modules won't be resolved at all.

If some relocation was applied (e.g. kexec) symbols may be misresolved
even with a suitable vmlinux or kallsyms file.

Cannot read kernel map
Couldn't record kernel reference relocation symbol
Symbol resolution may be skewed if relocation was used (e.g. kexec).
Check /proc/kallsyms permission or run as root.

Threads Returned!
Threads Joined!
Time: 2.932636 seconds
[ perf record: Woken up 5 times to write data ]
[ perf record: Captured and wrote 1.709 MB perf/apsp.cycles (44765 samples) ]

然后我像这样注释输出文件:

perf annotate --stdio -i perf/apsp.cycles --dsos=apsp

但在其中一个代码部分,我看到了一些奇怪的结果:

Percent |      Source code & Disassembly of apsp for L1-dcache-read-misses
---------------------------------------------------------------------------
         :               {
         :                  if((D[W_index[v][i]] > (D[v] + W[v][i])))
   19.36 :        401140:       movslq (%r10,%rcx,4),%rsi
   14.50 :        401144:       lea    (%rax,%rsi,4),%rdi
    1.22 :        401148:       mov    (%r9,%rcx,4),%esi
    5.82 :        40114c:       add    (%rax,%r8,4),%esi
   20.02 :        401150:       cmp    %esi,(%rdi)
    0.00 :        401152:       jle    401156 <do_work(void*)+0x226>
         :                     D[W_index[v][i]] = D[v] + W[v][i];
    9.72 :        401154:       mov    %esi,(%rdi)
   19.93 :        401156:       add    $0x1,%rcx
         :

现在在这些结果中,为什么一些算术指令有 L1 读取未命中?此外,为什么第二条语句的指令会导致如此多的缓存未命中,即使它们应该被前一个 if 语句带入缓存? 我在这里做错了吗?我在具有 root 访问权限的另一台机器上尝试了相同的操作,它给了我类似的结果,所以我认为我上面提到的警告并没有导致这种情况。但究竟发生了什么?

【问题讨论】:

  • 你能提供绝对数字吗?我认为这会很有帮助。我预计每次迭代的缓存未命中率略高于一次(D[W_index...]。Perf 可能会显示一些模糊的结果,但这种相对时间在这种情况下没有帮助。
  • @overseas 我不知道如何从性能中获取绝对数字。我用谷歌搜索了一下,我似乎找不到任何东西。如果我使用 perf list 所有列出的事件将输出百分比,就像我的问题中显示的那样。
  • 在此处查看此代码:pastebin.com/nr2mHmbS 您可能还想查看man perf_open_event
  • 要从 perf 中获取绝对数(函数中有多少样本),我们可以使用 -n 选项,但它仅适用于 perf report 命令,not for perf annotate 这就像一个错误.以-c 100000 等固定事件周期重新运行perf record 以获得稳定的样本数,使用perf report 获取函数的样本数,然后使用perf annotate 获取函数中的事件分布(100% 是总样本数当前函数)。

标签: c++ caching linux-kernel profiling perf


【解决方案1】:

所以我们有这个代码:

for(v=0;v<N;v++)
{
    for(int i = 0; i < DEG; i++)
    {
        if((/* (V2) 1000000000 * */ D[W_index[v][i]] > (D[v] + W[v][i])))
            D[W_index[v][i]] = D[v] + W[v][i];

        Q[v]=0; //Current vertex checked
    }
}

请注意,我在代码中添加了 (V2) 作为注释。下面我们回到这段代码。

一阶近似

记住W_index 被初始化为W_index[i][j] = i + j (A)

让我们关注一个内部迭代,首先让我们假设DEG 很大。此外,我们假设缓存足够大,可以容纳至少两次迭代的所有数据。

D[W_index[v][i]]

查找W_index[v] 被加载到寄存器中。对于W_index[v][i],我们假设有一次缓存未命中(64 字节缓存行,每个 int 4 字节,我们调用 DIM=16 的程序)。在D 中的查找总是从v 开始,所以大部分需要的数组部分已经在缓存中。假设DEG 很大,此查找是免费的。

D[v] + W[v][i]

查找D[v] 是免费的,因为它依赖于v。第二次查找同上,第二次缓存未命中。

整个内部语句没有影响。

Q[v]=0;

因为这是v,所以可以忽略。

当我们总结时,我们得到两个缓存未命中。

二次近似

现在,我们回到DEG 很大的假设。事实上这是错误的,因为DEG = 16。因此,我们还需要考虑部分缓存未命中。

D[W_index[v][i]]

查找W_index[v] 花费 1/8 的缓存未命中(它的大小为 8 字节,缓存行为 64 字节,因此我们在每 8 次迭代中得到一个缓存未命中)。

D[W_index[v][i]] 也是如此,除了 D 保存整数。平均而言,除了一个整数之外的所有整数都在缓存中,因此这会造成 1/16 的缓存未命中。

D[v] + W[v][i]

D[v] 已经在缓存中(这是W_index[v][0])。但是由于与上述相同的原因,我们得到了另外 1/8 的 W[v] 缓存未命中。

Q[v]=0;

这是另一个 1/16 的缓存未命中。

令人惊讶的是,如果我们现在使用 if-clause 永远不会计算为 true 的代码 (V2),我会在每次迭代中得到 2.395 次缓存未命中(请注意,您确实需要很好地配置 CPU,即如果可能,没有超线程,没有涡轮增压,性能调节器)。上面的计算将导致 2.375。所以我们很好。

三次近似

现在有这个不幸的if 子句。此比较多久评估一次true。我们不能说,一开始它会很频繁,最后它永远不会评估为true

所以让我们专注于完整循环的真正第一次执行。在这种情况下,D[v] 是无穷大,W[v][i] 是介于 1 和 101 之间的数字。因此,循环在每次迭代中计算为 true

然后它变得很难 - 我们在这个迭代中得到 2.9 个缓存未命中。它们来自哪里 - 所有数据都应该已经在缓存中。

但是:这是“编译器的奥秘”。你永远不知道他们最终生产了什么。我用 GCC 和 Clang 编译并得到相同的度量。我激活了-funroll-loops,突然我得到了 2.5 次缓存未命中。当然,这在您的系统上可能会有所不同。当我检查组件时,我发现它真的完全一样,只是循环展开了四次。

那么这告诉我们什么?你永远不知道你的编译器做了什么,除非你检查它。即使那样,你也不能确定。

我猜硬件预取或执行顺序可能会在这里产生影响。但这是个谜。

关于性能和您的问题

我认为你所做的测量有两个问题:

  • 它们是相对的,确切的线不是那么准确。
  • 您是多线程的,这可能更难跟踪。

我的经验是,当您想为代码的特定部分获得良好的度量时,您确实需要手动检查它。有时(并非总是)它可以很好地解释事情。

【讨论】:

  • 当你想从某事开始时,你可以拿这个。但这只是调试代码,所以我不会将它添加到答案中(当你看到它时,你就会明白为什么)pastebin.com/F7nDzrQi
猜你喜欢
  • 1970-01-01
  • 2017-10-03
  • 2019-07-28
  • 2016-10-04
  • 2014-07-28
  • 2012-09-07
  • 2015-01-13
  • 2018-05-28
  • 2014-06-29
相关资源
最近更新 更多