繁体   English   中英

Linux性能工具显示奇怪的缓存未命中结果

[英]Linux perf tool showing weird cache miss results

我正在使用linux perf工具来分析CRONO基准测试之一 ,我特别对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用户访问权限的另一台计算机上尝试了相同的操作,它给了我类似的结果,所以我认为我上面提到的警告并没有引起这种情况。 但是到底是怎么回事?

因此,我们有以下代码:

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字节,因此我们在每个第八次迭代中都会得到一个高速缓存未命中)。

D[W_index[v][i]] ,除了D保留整数。 平均而言,除一个整数外,所有整数都在高速缓存中,因此这花费了高速缓存未命中的1/16。

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

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

Q[v]=0;

这是缓存未命中率的另外1/16。

令人惊讶的是,如果我们现在使用代码(V2),其中if -clause永远不会评估为true ,那么每次迭代我都会遇到2.395高速缓存未命中(请注意,您确实需要很好地配置CPU,即没有超线程,没有turboboost,性能调速器)。 上面的计算将得出2.375。 所以我们很好。

第三近似

现在有一个不幸的if子句。 此比较多久一次会得出true 我们不能说,一开始它会经常出现,而到最后它永远不会评估为true

因此,让我们关注完整循环的真正首次执行。 在这种情况下, D[v]是无穷大, W[v][i]是1到101之间的一个数字。因此,循环在每次迭代中都为true

然后变得很困难-在此迭代中,我们得到2.9个缓存未命中。 它们来自何处-所有数据应该已经在缓存中。

但是 :这是“编译器之谜”。 您永远都不知道它们最终会产生什么。 我使用GCC和Clang进行编译,并得到相同的度量。 我激活-funroll-loops ,突然我得到2.5次缓存未命中。 当然,这在您的系统上可能有所不同。 当我检查装配时,我发现它实际上是完全相同的,只是循环已经展开了四次。

那这告诉我们什么呢? 除了检查之外,您永远都不知道编译器会做什么。 即使那样,您也不能确定。

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

关于性能及其问题

我认为您所做的测量存在两个问题:

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

我的经验是,当您想对代码的特定部分获得良好的衡量标准时,确实需要手动检查它。 有时(并非总是如此),它可以解释得很好。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM