CPU 后端流水线

在经过取指、解码为 μops、寄存器重命名、分配 ROB 条目后,μops 到达调度器。当数据就绪且执行端口空闲时,它就会被发射到后端的执行单元端口中:

  • INT Domain (整数域):ALU 负责加减乘除、位运算、跳转计算
  • FLOAT/VEC Domain (浮点/向量域):FPU/SIMD 单元负责浮点运算、AVX/SSE 指令
  • MEM Domain (访存域 / AGU):地址生成单元,负责计算 MOV RAX, [RBP+8] 中的内存地址

同时还有并行的 LSU(Load/Store Unit),负责 Load/Store 操作,让 MMU 将 AGU 计算出来的地址翻译为物理地址,接着与 Cache/DRAM 交互,进行地址的读取写入。


超标量与端口瓶颈

我们知道现代 CPU 都是 超标量 的,也就是有多条流水线、多执行单元,来实现单核并行。

后端端口的数量是单核并行能力的一个瓶颈,尤其是在计算密集型任务中。

本机配置 i9-12900,分为性能核和能效核,它们分别是:

核心类型 微架构 Load 端口 Store 端口
性能核 Golden Cove 3 个 2 个
能效核 Gracemont 2 个 2 个

数据来源:


测试方法

可以做一下简单的测试,核心测试代码如下:

Read_x1(1 个 Load)

1
2
3
4
5
6
7
8
9
10
Read_x1:
xor rsi, rsi
align 64
.loop:
mov rax, [rdx + rsi*8]
inc rsi
and rsi, 508
dec rcx
jnz .loop
ret

Read_x2(2 个 Load)

1
2
3
4
5
6
7
8
9
10
11
Read_x2:
xor rsi, rsi
align 64
.loop:
mov rax, [rdx + rsi*8]
mov rax, [rdx + rsi*8 + 8]
add rsi, 2
and rsi, 508
sub rcx, 2
jnle .loop
ret

Read_x3(3 个 Load)

1
2
3
4
5
6
7
8
9
10
11
12
Read_x3:
xor rsi, rsi
align 64
.loop:
mov rax, [rdx + rsi*8]
mov rax, [rdx + rsi*8 + 8]
mov rax, [rdx + rsi*8 + 16]
add rsi, 3
and rsi, 508
sub rcx, 3
jnle .loop
ret

Read_x4(4 个 Load)

1
2
3
4
5
6
7
8
9
10
11
12
13
Read_x4:
xor rsi, rsi
align 64
.loop:
mov rax, [rdx + rsi*8]
mov rax, [rdx + rsi*8 + 8]
mov rax, [rdx + rsi*8 + 16]
mov rax, [rdx + rsi*8 + 24]
add rsi, 4
and rsi, 508
sub rcx, 4
jnle .loop
ret

这是一个 循环展开 测试,在展开的每个循环里面有 1/2/3/4 个 Load。根据不同测试下带宽的提升,查看它运行的核上有多少个 Load 端口。


测试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
--- Read_x1 ---
Min: 823612664 (282.209757ms) 3.543464gb/s PF: 1487.0000 (705.1621k/fault)
Max: 1121666500 (384.337498ms) 2.601880gb/s PF: 1487.0000 (705.1621k/fault)
Avg: 888680952 (304.505317ms) 3.284015gb/s PF: 1486.7656 (705.2732k/fault)

--- Read_x2 ---
Min: 394226148 (135.081052ms) 7.402963gb/s PF: 1488.0000 (704.6882k/fault)
Max: 649297942 (222.481055ms) 4.494765gb/s PF: 1488.0000 (704.6882k/fault)
Avg: 454001531 (155.563006ms) 6.428264gb/s PF: 1491.8723 (702.8591k/fault)

--- Read_x3 ---
Min: 255890638 (87.680578ms) 11.405034gb/s PF: 1501.0000 (698.5849k/fault)
Max: 451388798 (154.667757ms) 6.465472gb/s PF: 1501.0000 (698.5849k/fault)
Avg: 296007480 (101.426560ms) 9.859350gb/s PF: 1501.0000 (698.5849k/fault)

--- Read_x4 ---
Min: 252895028 (86.654137ms) 11.540130gb/s PF: 1501.0000 (698.5849k/fault)
Max: 414795109 (142.128979ms) 7.035863gb/s PF: 1501.0000 (698.5849k/fault)
Avg: 285505918 (97.828214ms) 10.222000gb/s PF: 1501.0000 (698.5849k/fault)

Load Port Analysis

数据分析

根据测试出来的数据可以看到:

  • x1 → x2 → x3:基本是线性增长
  • x4:趋于平缓

那么它大概率是跑在了 性能核 上(3 个 Load 端口)。


Intel VTune Profiler 验证

使用 Intel VTune Profiler 查看硬件数据。

关键硬件事件统计(部分):

Event P-Core E-Core
CPU_CLK_UNHALTED.THREAD 283,825,900,000 536,500,000
INST_RETIRED.ANY 1,575,358,300,000 1,745,800,000
MEM_INST_RETIRED.ALL_LOADS 579,727,739,178 -
MEM_LOAD_RETIRED.L1_HIT 571,327,713,978 -
UOPS_DISPATCHED.PORT_2_3_10 579,306,868,959 -

VTune Analysis

结论

VTune 数据证实了我们的观点:程序 99.81% 运行在 P-Core (Golden Cove)。

从 UOPS_DISPATCHED.PORT_2_3_10 的数据(579B)与 Load 指令数量(579B)高度一致,验证了:

  • Golden Cove 的 Port 2/3/10 正是 3 个 Load 端口
  • x1 → x3 的线性增长符合 3 端口并行
  • x4 时饱和,因为只有 3 个端口可用