uops 词义解析
μops 代指 uop ,全称是 Micro-operation ,中文直译为“微操作”,micro 可以希腊字母 μ 代替,然后 operation 可以缩写为 op ,连一起即 μop。为方便书写,使用 u 代替 μ 。用 uops 来统称所有的 uop ,即 Micro-operations (为了方便,下文统一称为 uops )。
对于 x86 这种 CISC 处理器,为了能在内部更加高效灵活地处理指令,会将长度和复杂度不一的指令,转化成一个或多个 uop,以便更容易被调度和优化,从而达到提升 CPU 性能的目的。
x86 的指令可以理解成是粗颗粒度的,而 uop 可以理解成是细颗粒的微小指令。通过将复杂的粗粒度指令分解成简单的细粒度指令执行,变相获取了 RISC 架构的部分优势,让CPU的整体结构更加紧凑、灵活和高效。
uops 的优势
将指令分割成微操作的主要优点是:
乱序执行
以 PUSH rbx 指令为例,它将栈指针减少 8 字节,然后将源操作数存储在栈顶。假设在解码后 PUSH rbx 被“破解”成两个依赖的 uop:
SUB rsp, 8STORE [rsp], rbx
通常,函数序言通过使用多个 PUSH 指令保存多个寄存器。在我们的例子中,下一个 PUSH 指令可以在前一个 PUSH 指令的 SUB μop 完成后开始执行,而不必等待当前要执行的 STORE μop。
并行执行
以 HADDPD xmm1, xmm2 指令为例,它将在 xmm1 和 xmm2 中对两个双精度浮点数进行求和(减少),并将两个结果存储在 xmm1 中,如下所示:
xmm1[63:0] = xmm2[127:64] + xmm2[63:0]xmm1[127:64] = xmm1[127:64] + xmm1[63:0]
微代码化此指令的一种方法是执行以下操作:
- 减少 xmm2并将结果存储在 xmm_tmp1[63:0] 中;
- 减少 xmm1并将结果存储在 xmm_tmp2[63:0] 中;
- 将 xmm_tmp1 和 xmm_tmp2 合并到 xmm1 中。
总共三个 uop。对于步骤 1 和 2 是独立的,因此可以并行完成。
操作融合
有时 uops 也可以融合在一起,现代 CPU 中有两种类型的融合:
微融合:融合来自同一机器指令的 uops。微融合只能应用于两种类型的组合:内存写操作和读改操作。例如:
add eax, [mem]
这条指令中有两个 uops:
- 读取内存位置 mem;
- 将其添加到 eax。
使用微融合,在解码步骤中将两个 uops 融合成一个。
宏融合: 融合来自不同机器指令的 uops。在某些情况下,解码器可以将算术或逻辑指令与 subsequent 条件跳转指令融合成单个计算和分支 uop。例如:
.loop: dec rdi jnz .loop
使用宏融合,将来自 DEC 和 JNZ 指令的两个 uops 融合成一个。
微融合和宏融合都可以节省从解码到退休的所有管道阶段的带宽。融合操作在重新排序缓冲区 (ROB) 中共享单个条目。当一个融合的 uop 只使用一个条目时,ROB 的容量得到更好的利用。
统计 uops
要收集应用程序发出的、执行的和退休的 uops 数量,可以使用 Linux perf,如下所示:
$ perf stat -e uops_issued.any,uops_executed.thread,uops_retired.slots -- ./a.exe2856278 uops_issued.any2720241 uops_executed.thread2557884 uops_retired.slots
指令被分解成微操作的方式可能会随着 CPU 世代的不同而有所差异。通常,用于一条指令的 uops 数量越少,意味着硬件对其支持越好,并且可能具有更低的延迟和更高的吞吐量。对于最新的 Intel 和 AMD CPU,绝大多数指令都会生成恰好一个 uop。