这篇文章简单整理 LLVM-MCA 的使用思路。注意这只是个人的工具笔记,我关注这个工具的性能预测功能,而代码生成质量报告这些是直接忽略的。另外,如果只是想简单使用的话看几眼 --help 就行了,进一步了解完整的使用方法推荐直接看官方文档

基本使用

你可以直接在网页上运行 LLVM-MCA(也可以对比 uiCA、IACA 等竞品),我这里使用的本地环境。

先任意挑选一个简单的示例代码 mca_test.cpp来源):

int branch(int mStepSize, int ticks, bool running) {
    if (running) ticks += mStepSize;
    int x = ticks * 3;
    int y = ticks * x * 5;
    int z = ticks * 7 * x * y;
    return x + y + z;
}

编译器生成汇编后传递给 llvm-mca,命令为:

clang++ -S -O2 mca_test.cpp -o - | llvm-mca

输出结果:

warning: found a return instruction in the input assembly sequence.
note: program counter updates are ignored.
Iterations:        100
Instructions:      1200
Total Cycles:      1604
Total uOps:        1500

Dispatch Width:    6
uOps Per Cycle:    0.94
IPC:               0.75
Block RThroughput: 2.5


Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)

[1]    [2]    [3]    [4]    [5]    [6]    Instructions:
 1      1     0.25                        testl %edx, %edx
 1      1     0.50                        cmovel        %edx, %edi
 1      1     0.25                        addl  %esi, %edi
 2      2     0.25                        leal  (%rdi,%rdi,2), %ecx
 1      3     1.00                        imull %ecx, %edi
 2      2     0.25                        leal  (%rdi,%rdi,4), %eax
 1      3     1.00                        imull %eax, %edi
 2      2     0.25                        leal  (,%rdi,8), %edx
 1      1     0.25                        subl  %edi, %edx
 1      1     0.25                        addl  %ecx, %eax
 1      1     0.25                        addl  %edx, %eax
 1      5     0.50                  U     retq


Resources:
[0]   - Zn3AGU0
[1]   - Zn3AGU1
[2]   - Zn3AGU2
[3]   - Zn3ALU0
[4]   - Zn3ALU1
[5]   - Zn3ALU2
[6]   - Zn3ALU3
[7]   - Zn3BRU1
[8]   - Zn3FPP0
[9]   - Zn3FPP1
[10]  - Zn3FPP2
[11]  - Zn3FPP3
[12.0] - Zn3FPP45
[12.1] - Zn3FPP45
[13]  - Zn3FPSt
[14.0] - Zn3LSU
[14.1] - Zn3LSU
[14.2] - Zn3LSU
[15.0] - Zn3Load
[15.1] - Zn3Load
[15.2] - Zn3Load
[16.0] - Zn3Store
[16.1] - Zn3Store


Resource pressure per iteration:
[0]    [1]    [2]    [3]    [4]    [5]    [6]    [7]    [8]    [9]    [10]   [11]   [12.0] [12.1] [13]   [14.0] [14.1] [14.2] [15.0] [15.1] [15.2] [16.0] [16.1]
0.33   0.33   0.34   2.68   3.02   2.67   2.67   0.96    -      -      -      -      -      -      -     0.33   0.33   0.34   0.33   0.33   0.34    -      -

Resource pressure by instruction:
[0]    [1]    [2]    [3]    [4]    [5]    [6]    [7]    [8]    [9]    [10]   [11]   [12.0] [12.1] [13]   [14.0] [14.1] [14.2] [15.0] [15.1] [15.2] [16.0] [16.1] Instructions:
 -      -      -     0.01   0.32   0.33   0.34    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     testl  %edx, %edx
 -      -      -     0.02    -      -     0.98    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     cmovel %edx, %edi
 -      -      -     0.32   0.33   0.35    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     addl   %esi, %edi
 -      -      -     0.33   0.35   0.32    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     leal   (%rdi,%rdi,2), %ecx
 -      -      -      -     1.00    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     imull  %ecx, %edi
 -      -      -     0.65    -     0.33   0.02    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     leal   (%rdi,%rdi,4), %eax
 -      -      -      -     1.00    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     imull  %eax, %edi
 -      -      -     0.32    -     0.33   0.35    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     leal   (,%rdi,8), %edx
 -      -      -     0.33    -     0.35   0.32    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     subl   %edi, %edx
 -      -      -     0.33    -     0.34   0.33    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     addl   %ecx, %eax
 -      -      -     0.33   0.02   0.32   0.33    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     addl   %edx, %eax
0.33   0.33   0.34   0.04    -      -      -     0.96    -      -      -      -      -      -      -     0.33   0.33   0.34   0.33   0.33   0.34    -      -     retq

关键指标

Iterations:        100
Instructions:      1200
Total Cycles:      1604
Total uOps:        1500

Dispatch Width:    6
uOps Per Cycle:    0.94
IPC:               0.75
Block RThroughput: 2.5

也许用户最关心的就是位于顶部的指标,这些是性能预测报告的总览。

Iterations 表示指令模拟迭代的次数,默认会重复执行 100 遍,可以使用 --iterations 调整参数。接下来三个 InstructionsTotal CyclesTotal uOps 是字面意思的统计信息,表示总共有多少个指令、周期以及微指令。

Dispatch Width 表示流水线的宽度,LLVM-MCA 如果不指定 arch,那么有些参数是根据实机使用的 CPU 选择对应的调度模型,我在这里使用的是 ZEN3 架构的调度模型,默认宽度为 6,也可以使用 --dispatch 调整参数。接下来两个指标表示单指令的性能,除了 IPC 以外还可以精细到微指令层面 uOps Per Cycle。最后一个 Block RThroughput 表示整个代码块(基本块)的吞吐量倒数,指标越低即整体吞吐量越高。

通常在比较两份代码时,可以直接看 Block RThroughput,这就是一般意义上的性能指标(谁跑得快)。同时 IPC 或者 uOps Per Cycle 的参考价值就没那么大,因为可以做到单指令效率低但是块整体效率更高(比如指令数减少了)。也可通过计算 Dispatch Width - uOps Per Cycle 的差值了解流水线的空置程度,大致意思是理想状态下还有多少的优化空间。

// -O0
Iterations:        100
Instructions:      2700
Total Cycles:      2004
Total uOps:        2700

Dispatch Width:    6
uOps Per Cycle:    1.35
IPC:               1.35
Block RThroughput: 10.0

作为对比,上面是 -O0 优化等级代码的分析报告,而我们示例用的是 -O2,谁跑得快一目了然。

指令分析

Instruction Info:
[1]: #uOps
[2]: Latency
[3]: RThroughput
[4]: MayLoad
[5]: MayStore
[6]: HasSideEffects (U)

[1]    [2]    [3]    [4]    [5]    [6]    Instructions:
 1      1     0.25                        testl %edx, %edx
 1      1     0.50                        cmovel        %edx, %edi
 1      1     0.25                        addl  %esi, %edi
 2      2     0.25                        leal  (%rdi,%rdi,2), %ecx
 1      3     1.00                        imull %ecx, %edi
 2      2     0.25                        leal  (%rdi,%rdi,4), %eax
 1      3     1.00                        imull %eax, %edi
 2      2     0.25                        leal  (,%rdi,8), %edx
 1      1     0.25                        subl  %edi, %edx
 1      1     0.25                        addl  %ecx, %eax
 1      1     0.25                        addl  %edx, %eax
 1      5     0.50                  U     retq

这里会把前面的概括信息进一步展示,[4][5][6] 对于 LLVM-MCA 来说都是不确定因素,因为 LLVM-MCA 不能感知到精确的内存和缓存信息。这是提示预测可能不准确(打星号 *)的意思。

另外,这里用的示例代码是比较偷懒的。LLVM-MCA 其实对于 call 和 ret 指令是直接报出警告的,基本无法分析。因此像这样的静态分析工具都是用来看一小段代码的。

资源压力

Resources:
[0]   - Zn3AGU0
[1]   - Zn3AGU1
[2]   - Zn3AGU2
[3]   - Zn3ALU0
[4]   - Zn3ALU1
[5]   - Zn3ALU2
[6]   - Zn3ALU3
[7]   - Zn3BRU1
[8]   - Zn3FPP0
[9]   - Zn3FPP1
[10]  - Zn3FPP2
[11]  - Zn3FPP3
[12.0] - Zn3FPP45
[12.1] - Zn3FPP45
[13]  - Zn3FPSt
[14.0] - Zn3LSU
[14.1] - Zn3LSU
[14.2] - Zn3LSU
[15.0] - Zn3Load
[15.1] - Zn3Load
[15.2] - Zn3Load
[16.0] - Zn3Store
[16.1] - Zn3Store


Resource pressure per iteration:
[0]    [1]    [2]    [3]    [4]    [5]    [6]    [7]    [8]    [9]    [10]   [11]   [12.0] [12.1] [13]   [14.0] [14.1] [14.2] [15.0] [15.1] [15.2] [16.0] [16.1]
0.33   0.33   0.34   2.68   3.02   2.67   2.67   0.96    -      -      -      -      -      -      -     0.33   0.33   0.34   0.33   0.33   0.34    -      -

Resource pressure by instruction:
[0]    [1]    [2]    [3]    [4]    [5]    [6]    [7]    [8]    [9]    [10]   [11]   [12.0] [12.1] [13]   [14.0] [14.1] [14.2] [15.0] [15.1] [15.2] [16.0] [16.1] Instructions:
 -      -      -     0.01   0.32   0.33   0.34    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     testl  %edx, %edx
 -      -      -     0.02    -      -     0.98    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     cmovel %edx, %edi
 -      -      -     0.32   0.33   0.35    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     addl   %esi, %edi
 -      -      -     0.33   0.35   0.32    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     leal   (%rdi,%rdi,2), %ecx
 -      -      -      -     1.00    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     imull  %ecx, %edi
 -      -      -     0.65    -     0.33   0.02    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     leal   (%rdi,%rdi,4), %eax
 -      -      -      -     1.00    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     imull  %eax, %edi
 -      -      -     0.32    -     0.33   0.35    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     leal   (,%rdi,8), %edx
 -      -      -     0.33    -     0.35   0.32    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     subl   %edi, %edx
 -      -      -     0.33    -     0.34   0.33    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     addl   %ecx, %eax
 -      -      -     0.33   0.02   0.32   0.33    -      -      -      -      -      -      -      -      -      -      -      -      -      -      -      -     addl   %edx, %eax
0.33   0.33   0.34   0.04    -      -      -     0.96    -      -      -      -      -      -      -     0.33   0.33   0.34   0.33   0.33   0.34    -      -     retq

省流:资源压力只要是数值分布均匀,那就是正常的。

这些指令(除了 ret 以外)都是在 ALU 上竞争资源,但数值也没高到哪里去(文档提到是基于 RR 进行分摊的),合理。

时间线

使用 llvm-mca --timeline 可以分析指令的调度情况。从而推断是数据依赖瓶颈还是硬件资源瓶颈。

Timeline view:
                    0123456789          0123456789          0123456789          0123456789
Index     0123456789          0123456789          0123456789          0123456789

[0,0]     DeER .    .    .    .    .    .    .    .    .    .    .    .    .    .    .   .   testl      %edx, %edx
[0,1]     D=eER.    .    .    .    .    .    .    .    .    .    .    .    .    .    .   .   cmovel     %edx, %edi
[0,2]     D==eER    .    .    .    .    .    .    .    .    .    .    .    .    .    .   .   addl       %esi, %edi
[0,3]     D===eeER  .    .    .    .    .    .    .    .    .    .    .    .    .    .   .   leal       (%rdi,%rdi,2), %ecx
[0,4]     D=====eeeER    .    .    .    .    .    .    .    .    .    .    .    .    .   .   imull      %ecx, %edi
[0,5]     .D=======eeER  .    .    .    .    .    .    .    .    .    .    .    .    .   .   leal       (%rdi,%rdi,4), %eax
[0,6]     .D=========eeeER    .    .    .    .    .    .    .    .    .    .    .    .   .   imull      %eax, %edi
[0,7]     .D============eeER  .    .    .    .    .    .    .    .    .    .    .    .   .   leal       (,%rdi,8), %edx
[0,8]     .D==============eER .    .    .    .    .    .    .    .    .    .    .    .   .   subl       %edi, %edx
[0,9]     . D========eE-----R .    .    .    .    .    .    .    .    .    .    .    .   .   addl       %ecx, %eax
[0,10]    . D==============eER.    .    .    .    .    .    .    .    .    .    .    .   .   addl       %edx, %eax
[0,11]    . DeeeeeE----------R.    .    .    .    .    .    .    .    .    .    .    .   .   retq
[1,0]     . D==============eER.    .    .    .    .    .    .    .    .    .    .    .   .   testl      %edx, %edx
[1,1]     . D===============eER    .    .    .    .    .    .    .    .    .    .    .   .   cmovel     %edx, %edi
[1,2]     . D================eER   .    .    .    .    .    .    .    .    .    .    .   .   addl       %esi, %edi
[1,3]     .  D================eeER .    .    .    .    .    .    .    .    .    .    .   .   leal       (%rdi,%rdi,2), %ecx
[1,4]     .  D==================eeeER   .    .    .    .    .    .    .    .    .    .   .   imull      %ecx, %edi
[1,5]     .  D=====================eeER .    .    .    .    .    .    .    .    .    .   .   leal       (%rdi,%rdi,4), %eax
[1,6]     .  D=======================eeeER   .    .    .    .    .    .    .    .    .   .   imull      %eax, %edi
[1,7]     .   D=========================eeER .    .    .    .    .    .    .    .    .   .   leal       (,%rdi,8), %edx
[1,8]     .   D===========================eER.    .    .    .    .    .    .    .    .   .   subl       %edi, %edx
[1,9]     .   D======================eE-----R.    .    .    .    .    .    .    .    .   .   addl       %ecx, %eax
[1,10]    .   D============================eER    .    .    .    .    .    .    .    .   .   addl       %edx, %eax
[1,11]    .   DeeeeeE------------------------R    .    .    .    .    .    .    .    .   .   retq
[2,0]     .    D===========================eER    .    .    .    .    .    .    .    .   .   testl      %edx, %edx
[2,1]     .    D============================eER   .    .    .    .    .    .    .    .   .   cmovel     %edx, %edi
[2,2]     .    D=============================eER  .    .    .    .    .    .    .    .   .   addl       %esi, %edi
[2,3]     .    D==============================eeER.    .    .    .    .    .    .    .   .   leal       (%rdi,%rdi,2), %ecx
[2,4]     .    D================================eeeER  .    .    .    .    .    .    .   .   imull      %ecx, %edi
[2,5]     .    .D==================================eeER.    .    .    .    .    .    .   .   leal       (%rdi,%rdi,4), %eax
[2,6]     .    .D====================================eeeER  .    .    .    .    .    .   .   imull      %eax, %edi
[2,7]     .    .D=======================================eeER.    .    .    .    .    .   .   leal       (,%rdi,8), %edx
[2,8]     .    .D=========================================eER    .    .    .    .    .   .   subl       %edi, %edx
[2,9]     .    . D===================================eE-----R    .    .    .    .    .   .   addl       %ecx, %eax
[2,10]    .    . D=========================================eER   .    .    .    .    .   .   addl       %edx, %eax
[2,11]    .    . DeeeeeE-------------------------------------R   .    .    .    .    .   .   retq
[3,0]     .    . D=========================================eER   .    .    .    .    .   .   testl      %edx, %edx
[3,1]     .    . D==========================================eER  .    .    .    .    .   .   cmovel     %edx, %edi
[3,2]     .    . D===========================================eER .    .    .    .    .   .   addl       %esi, %edi
[3,3]     .    .  D===========================================eeER    .    .    .    .   .   leal       (%rdi,%rdi,2), %ecx
[3,4]     .    .  D=============================================eeeER .    .    .    .   .   imull      %ecx, %edi
[3,5]     .    .  D================================================eeER    .    .    .   .   leal       (%rdi,%rdi,4), %eax
[3,6]     .    .  D==================================================eeeER .    .    .   .   imull      %eax, %edi
[3,7]     .    .   D====================================================eeER    .    .   .   leal       (,%rdi,8), %edx
[3,8]     .    .   D======================================================eER   .    .   .   subl       %edi, %edx
[3,9]     .    .   D=================================================eE-----R   .    .   .   addl       %ecx, %eax
[3,10]    .    .   D=======================================================eER  .    .   .   addl       %edx, %eax
[3,11]    .    .   DeeeeeE---------------------------------------------------R  .    .   .   retq
[4,0]     .    .    D======================================================eER  .    .   .   testl      %edx, %edx
[4,1]     .    .    D=======================================================eER .    .   .   cmovel     %edx, %edi
[4,2]     .    .    D========================================================eER.    .   .   addl       %esi, %edi
[4,3]     .    .    D=========================================================eeER   .   .   leal       (%rdi,%rdi,2), %ecx
[4,4]     .    .    D===========================================================eeeER.   .   imull      %ecx, %edi
[4,5]     .    .    .D=============================================================eeER  .   leal       (%rdi,%rdi,4), %eax
[4,6]     .    .    .D===============================================================eeeER   imull      %eax, %edi
Truncated display due to cycle limit


Average Wait times (based on the timeline view):
[0]: Executions
[1]: Average time spent waiting in a scheduler's queue
[2]: Average time spent waiting in a scheduler's queue while ready
[3]: Average time elapsed from WB until retire stage

      [0]    [1]    [2]    [3]
0.     10    62.0   0.1    0.0       testl      %edx, %edx
1.     10    63.0   0.0    0.0       cmovel     %edx, %edi
2.     10    64.0   0.0    0.0       addl       %esi, %edi
3.     10    64.5   0.0    0.0       leal       (%rdi,%rdi,2), %ecx
4.     10    66.5   0.0    0.0       imull      %ecx, %edi
5.     10    69.0   0.0    0.0       leal       (%rdi,%rdi,4), %eax
6.     10    71.0   0.0    0.0       imull      %eax, %edi
7.     10    73.5   0.0    0.0       leal       (,%rdi,8), %edx
8.     10    75.5   0.0    0.0       subl       %edi, %edx
9.     10    70.0   0.0    5.0       addl       %ecx, %eax
10.    10    76.0   0.0    0.0       addl       %edx, %eax
11.    10    1.0    1.0    71.0      retq
       10    63.0   0.1    6.3       <total>

Index 是一对的,比如 [1, 2] 就表示第 1 次迭代和第 2 条指令(首次从 0 算起)。

接下来的一串字符就是具体的调度执行:

  • D: 表示分发指令(dispatch)。
  • =: 表示等待执行。
  • e: 表示执行指令(execute)。
  • E: 表示执行完成。
  • -: 表示等待退役。
  • R: 表示退役(retire)。

NOTE: LLVM-MCA 不考虑完整的 CPU 前端(应该是很难做到?),所以没有取码(fetch)和译码(decode)过程的模拟。

很显然,LLVM-MCA 是支持乱序执行的,比如 [0, 9] 的执行开始时机是比 [0, 8] 还要早。但是仍需确保顺序退役(program order),所以需要等待 [0, 8] 退役。

另外提下分发指令的条件。看着比较复杂,依赖于调度模型。文档提到具体的分发需要满足四个条件:

  1. 分发组(dispatch group,大致意思是并行分发的多个微指令)大小要满足流水线宽度的限制。
  2. 重排序缓冲(ROB)中有足够的条目。
  3. 物理寄存器有足够的数量做寄存器重命名(不退役会一直占着物理寄存器)。
  4. 调度器未达到满容量。(Each processor scheduler implements a buffer of instructions.)

具体可以观察 --dispatch-stats,解释比较长就折叠了:

点击展开
// 简化起见设 `--iterations=1`

// 这里有分发停顿报告,可惜文档没有详细说明
Dynamic Dispatch Stall Cycles:
RAT     - Register unavailable:                      0
RCU     - Retire tokens unavailable:                 0
SCHEDQ  - Scheduler full:                            0
LQ      - Load queue full:                           0
SQ      - Store queue full:                          0
GROUP   - Static restrictions on the dispatch group: 0
USH     - Uncategorised Structural Hazard:           0

// 分发并行程度的报告(0 除外)
// 我们当然是希望分发的并行程度越高越好
// 看下面时间线加以说明
Dispatch Logic - number of cycles where we saw N micro opcodes dispatched:
[# dispatched], [# cycles]
 0,              17  (85.0%)
 3,              1  (5.0%)
 6,              2  (10.0%)

// 注意之前的 Instruction Info 中的 #uOPs 指标,猜你也懒得翻,指令后面加上注释了
// 我在下面还添加了箭头(每一个 ^ 对应一个时钟周期),用来说明每一个分发并行是怎么统计的
Timeline view:
                    0123456789
Index     0123456789

[0,0]     DeER .    .    .   .   testl  %edx, %edx // 这里大部分指令都是 1 uOP
[0,1]     D=eER.    .    .   .   cmovel %edx, %edi
[0,2]     D==eER    .    .   .   addl   %esi, %edi
[0,3]     D===eeER  .    .   .   leal   (%rdi,%rdi,2), %ecx // 但是 leal 指令有 2 个 uOP
[0,4]     D=====eeeER    .   .   imull  %ecx, %edi // [0,0]~[0,4] 加起来的 uOP 数目为 6
[0,5]     .D=======eeER  .   .   leal   (%rdi,%rdi,4), %eax
[0,6]     .D=========eeeER   .   imull  %eax, %edi
[0,7]     .D============eeER .   leal   (,%rdi,8), %edx
[0,8]     .D==============eER.   subl   %edi, %edx
[0,9]     . D========eE-----R.   addl   %ecx, %eax
[0,10]    . D==============eER   addl   %edx, %eax
[0,11]    . DeeeeeE----------R   retq
          ^^
          ||^
          |||^^^^^^^^^^^^^^^^^
6 dispatched|  0 dispatched
            |
            3 dispatched

再往下就是等待时间的分析。[0] 指的是指令的执行次数,注意虽然前面提到迭代是默认 100 次,但是 timeline 有其它调整参数限制了时钟周期总数,所以这里限制为只有 10 次迭代。

[1][2][3] 可以依靠上面的调度情况算出来。简单来说:

  • [1] 表示队列的等待时间,计算方式为 D 加上 = 的次数的平均值。
  • [2] 表示已就绪(ready)后的队列的等待时间,计算方式为 ready 到首个 e 的时间差的平均值。
  • [3] 表示回写到退役时间,计算方式为 E 加上 - 的次数的平均值。

NOTE: ready 状态在上表是看不出来的。ready 状态的指令可以出现在 = 的任意时间,也可以是与首个 e(表示 issued)重叠。前者可能出现的情况可能是调度(比如硬件 port)资源暂时不够用。如果真的想看 ready 下标,也可以通过 --json 选项打印出来。

[1] - [2] 的差值可以体现指令对数据依赖的程度,[2] 本身可以体现硬件资源压力的程度,[3] 应该说明不了什么,可能是表达一种乱序的程度(这是我个人觉得,因为只有乱序程度足够高,执行时机足够提前,才能使得回写和退役的时间差拉得足够开)。

很显然在这里硬件资源是没什么压力的,相比之下数据依赖就是重灾区。

瓶颈分析

使用 llvm-mca --bottleneck-analysis 可以帮你自动化分析上述报告。

Cycles with backend pressure increase [ 87.09% ]
Throughput Bottlenecks:
  Resource Pressure       [ 0.00% ]
  Data Dependencies:      [ 87.09% ]
  - Register Dependencies [ 87.09% ]
  - Memory Dependencies   [ 0.00% ]

Critical sequence based on the simulation:

              Instruction                                 Dependency Information
 +----< 8.    subl      %edi, %edx
 |
 |    < loop carried >
 |
 +----> 0.    testl     %edx, %edx                        ## REGISTER dependency:  %edx
 +----> 1.    cmovel    %edx, %edi                        ## REGISTER dependency:  %flags
 +----> 2.    addl      %esi, %edi                        ## REGISTER dependency:  %edi
 +----> 3.    leal      (%rdi,%rdi,2), %ecx               ## REGISTER dependency:  %edi
 +----> 4.    imull     %ecx, %edi                        ## REGISTER dependency:  %ecx
 +----> 5.    leal      (%rdi,%rdi,4), %eax               ## REGISTER dependency:  %edi
 +----> 6.    imull     %eax, %edi                        ## REGISTER dependency:  %eax
 |      7.    leal      (,%rdi,8), %edx
 +----> 8.    subl      %edi, %edx                        ## REGISTER dependency:  %edi
 |      9.    addl      %ecx, %eax
 +----> 10.   addl      %edx, %eax                        ## REGISTER dependency:  %edx
        11.   retq

这里很直接告诉你是数据依赖造成的压力,并且是前后依赖寄存器中的数据导致的。

毕竟示例代码本身就是就是一种强数据依赖的算法。


剩下的吃个饭再写。保证尽量不鸽!