【发布时间】:2016-12-13 04:58:23
【问题描述】:
在回答我的问题The advantages of using 32bit registers/instructions in x86-64 时,我开始衡量指令的成本。我知道这已经多次完成(例如Agner Fog),但我这样做是为了娱乐和自我教育。
我的测试代码非常简单(为了简单起见,这里是伪代码,实际上是在汇编程序中):
for(outer_loop=0; outer_loop<NO;outer_loop++){
operation #first
operation #second
...
operation #NI-th
}
但还是需要考虑一些事情。
- 如果循环的内部部分很大(大
NI>10^7),则循环的整个内容都无法放入指令缓存中,因此必须一遍又一遍地加载,从而使RAM的速度决定了时间执行所需。例如,对于大型内部零件,xorl %eax, %eax(2 字节)比xorq %rax, %rax(3 字节)快 33%。 - 如果
NI很小并且整个循环很容易放入指令缓存中,那么xorl %eax, %eax和xorq %rax, %rax速度相同,每个时钟周期可以执行4 次。
但是,这个简单的模型不适用于jmp-指令。对于jmp-指令,我的测试代码如下所示:
for(outer_loop=0; outer_loop<NO;outer_loop++){
jmp .L0
.L0: jmp .L1
L1: jmp L2
....
}
结果是:
- 对于“大”循环大小(已用于
NI>10^4),我测量 4.2 ns/jmp-instruction(相当于从 RAM 加载 42 个字节或在我的机器上大约 12 个时钟周期)。 - 对于小循环大小 (
NI<10^3),我测量为 1 ns/jmp-instruction(大约 3 个时钟周期,这听起来很合理 - Agner Fog 的表格显示成本为 2 个时钟周期)。
指令jmp LX使用2字节eb 00编码。
因此,我的问题是:“大”循环中jmp-instruction 的高成本可能是什么原因?
PS:如果你想在你的机器上试用它,你可以从here下载脚本,在src文件夹中运行sh jmp_test.sh。
编辑:实验结果证实了彼得的 BTB 大小理论。
下表显示了不同ǸI 值(相对于NI=1000)的每条指令周期:
|oprations/ NI | 1000 | 2000| 3000| 4000| 5000| 10000|
|---------------------|------|------|------|------|------|------|
|jmp | 1.0 | 1.0 | 1.0 | 1.2 | 1.9 | 3.8|
|jmp+xor | 1.0 | 1.2 | 1.3 | 1.6 | 2.8 | 5.3|
|jmp+cmp+je (jump) | 1.0 | 1.5 | 4.0 | 4.4 | 5.5 | 5.5|
|jmp+cmp+je (no jump) | 1.0 | 1.2 | 1.3 | 1.5 | 3.8 | 7.6|
可见:
- 对于
jmp指令,(未知的)资源变得稀缺,这导致大于 4000 的ǸI的性能下降。 - 此资源不与诸如
xor之类的指令共享 - 如果jmp和xor相继执行,则NI的性能下降仍然会持续大约4000。 - 但是如果进行跳转,则此资源与
je共享 - 对于jmp+je,资源在NI之间变得稀缺大约2000 年。 - 但是,如果
je根本不跳转,则资源再次变得稀缺,NI大约为 4000(第 4 行)。
Matt Godbolt's branch-prediction reverse engineering articles 确定分支目标缓冲区容量为 4096 个条目。这是一个非常有力的证据,表明 BTB 未命中是观察到的小型和大型 jmp 循环之间的吞吐量差异的原因。
【问题讨论】:
-
名称在调试信息中。发布的可执行文件在任何地方都没有标签名称。
-
请注意,
xorq %rax,%rax与xorl %eax,%eax的作用完全相同,因此几乎没有理由使用前者(除非可能避免在某处插入nop进行对齐)。 -
您的“大型”10,000 条指令循环很容易适应现代处理器 (256K) 的二级缓存,因此您无需测量 RAM 的速度。
-
@RossRidge 你是对的,对于
mov和xor,我需要在循环中执行 10^7 指令才能看到“RAM 速度”。然而jmp从 10^3 到 10^4 变慢了 4 倍。我并不是说这是因为 RAM - 它是不同的东西,但我不太清楚它是什么。 -
您可能已经理解了它(因为您首先编写了该测试用例),但它可能需要明确 - 您的
jmp+cmp+je (no jump)案例直到大约 4,000 次跳转才会达到资源稀缺的原因是因为未进行的跳转不会消耗 BTB 条目(实际上,BTB 中不会有任何内容!)。
标签: assembly x86 intel cpu-architecture branch-prediction