机器码分析器

机器码分析器(machine code analyzer)是一种程序,它提取一小段汇编代码,使用编译器可用的信息模拟其在特定微架构上的执行,并输出整个块的延迟和吞吐量,以及CPU内各种资源的完美周期(cycle-perfect,指可以仿真可以完美还原原处理器每个时钟周期的行为)利用率。

使用llvm-mca

有很多种机器代分析器,但我个人更喜欢llvm-mca,你可以通过包管理器和clang一起安装它。也可以通过名为UICA的web工具访问它,或者在 Compiler Explorer 中语言一栏选择“Analysis”。

llvm-mca所做的是,对给定的汇编片段执行一定次数的迭代,并计算每条相关指令的资源使用情况的统计信息,这对于找出瓶颈所在非常有用。

考虑数组求和的简单示例:

loop:
addl (%rax), %edx
addq $4, %rax
cmpq %rcx, %rax
jne loop

以下是llvm-mca在Skylake架构下的分析结果:

Iterations:        100
Instructions: 400
Total Cycles: 108
Total uOps: 500

Dispatch Width: 6
uOps Per Cycle: 4.63
IPC: 3.70
Block RThroughput: 0.8

首先,它输出关于循环和硬件的一般信息:

  • 它“运行”了100次迭代,在108个时钟周期内总共执行了400条指令,这与平均每个周期执行400/1073.7400/107≈3.7条指令是一致的(instructions per cycle,IPC)。
  • 理论上,CPU每个周期最多可以执行6条指令(调度宽度(dispatch width))。
  • 理论上,平均每轮迭代可以在0.8个时钟周期内执行完成(代码块的吞吐量倒数)。
  • 这里的“uOps”是CPU将每条指令拆分为的微操作(例如,融合的load-add由两个uOps组成)。

然后,它继续给出单独每个指令的信息:

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

[1] [2] [3] [4] [5] [6] Instructions:
2 6 0.50 * addl (%rax), %edx
1 1 0.25 addq $4, %rax
1 1 0.25 cmpq %rcx, %rax
1 1 0.50 jne -11

这里全都是指令表所包含的内容:

  • 每个指令被分成多少个uOps;

  • 每条指令需要多少个周期才能完成(延迟);

  • 考虑到可以同时执行每条指令的多个副本(多发射),每条指令平均需要多少个周期才能完成(吞吐量倒数)。

然后它输出可能是最重要的部分——每条指令在何时何地执行:

Resource pressure by instruction:
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] Instructions:
- - 0.01 0.98 0.50 0.50 - - 0.01 - addl (%rax), %edx
- - - - - - - 0.01 0.99 - addq $4, %rax
- - - 0.01 - - - 0.99 - - cmpq %rcx, %rax
- - 0.99 - - - - - 0.01 - jne -11

由于对执行端口的竞争会导致结构冒险,因此执行端口通常会成为吞吐量密集型的迭代的瓶颈,此图表有助于分析原因。它不会给你一个类似完美周期甘特图的东西,但它会给你每个指令所用执行端口的汇总统计信息,让你找到哪一个端口过载了。