我正在研究我的 Haswell 端口 0 上的分支单元的功能,从一个非常简单的循环开始:
BITS 64
GLOBAL _start
SECTION .text
_start:
mov ecx, 10000000
.loop:
dec ecx ;|
jz .end ;| 1 uOP (call it D)
jmp .loop ;| 1 uOP (call it J)
.end:
mov eax, 60
xor edi, edi
syscall
使用perf
我们看到循环以 1c/iter 运行
Performance counter stats for './main' (50 runs):
10,001,055 uops_executed_port_port_6 ( +- 0.00% )
9,999,973 uops_executed_port_port_0 ( +- 0.00% )
10,015,414 cycles:u ( +- 0.02% )
23 resource_stalls_rs ( +- 64.05% )
我对这些结果的解释是:
- D 和 J 都是并行调度的。
- J 具有 1 个周期的倒数吞吐量。
- D 和 J 都被最优调度。
但是,我们也可以看到 RS 永远不会被填满。
它最多可以以 2 uOPs/c 的速率调度 uOP,但理论上可以得到 4 uOPs/c,从而在大约 30 c 内实现完整的 RS(对于大小为 60 个融合域条目的 RS)。
据我了解,分支错误预测应该很少,uOP 应该都来自 LSD。
所以我看了一下FE:
8,239,091 lsd_cycles_active ( +- 3.10% )
989,320 idq_dsb_cycles ( +- 23.47% )
2,534,972 idq_mite_cycles ( +- 15.43% )
4,929 idq_ms_uops ( +- 8.30% )
0.007429733 seconds time elapsed ( +- 1.79% )
这确认了 FE 是从 LSD 1发出的。
但是,LSD 从不发出 4 uOPs/c:
7,591,866 lsd_cycles_active ( +- 3.17% )
0 lsd_cycles_4_uops
我的解释是 LSD 不能从下一次迭代2发出 uOP,因此每个周期只能将 DJ 对发送到 BE。
我的解释正确吗?
源代码在这个存储库中。
1存在一些差异,我认为这是由于允许某些上下文切换的大量迭代。
2在电路深度有限的硬件中,这听起来相当复杂。