如何解读uops.info?

How to interpret uops.info?

我在 uops.info 上查找了指令 VMOVDQA,试图弄清楚 (1) 什么是延迟,以及 (2) 我可以执行多少个并发加载?

我无法解释结果(下面的屏幕截图,上面也有链接):

非常感谢任何对此的指点!

如果 运行 宁 的大块只是 该指令,则吞吐量是倒数吞吐量。 (或者对于 adcdiv 等情况下的 dependency-breaking 指令,由于隐式寄存器 inputs/outputs,您无法使 back-to-back 执行不具有数据依赖性,特别是旗帜)。所以 0.5 意味着它可以 运行 每 0.5 个周期一次,即 2/clock,正如我们所知道的具有 2 个加载端口的 CPU 所预期的那样。

Why are there sometimes two numbers for latency, e.g. [≤10;≤11]?

另请参阅,其中以加载+ALU ALU 指令为例。 (我忘记了重复的有多接近,直到我写完这个答案的其余部分才寻找它。)

通常这表示从不同输入到输出的延迟可能不同。例如merge-masking 加载必须合并到目标中,因此这是一个输入,加载地址是另一个输入(通过整数寄存器)。内存中的 recently-stored 数据是第三个输入(store-forwarding 延迟)。

对于向量 load-use 延迟这样的情况,其中加载结果与地址寄存器在不同的域中,uops.info 创建一个依赖链,其指令序列涉及 movdvmovq rax, xmm0 将加载结果耦合回另一个加载的地址。很难分别为每个部分建立延迟,因此 IIRC 他们假设链中的每个其他指令至少为 1 个周期,并将被测指令的延迟显示为 <= N,其中 N + 其余部分dep 链加起来就是测试代码每次迭代的总周期数。

查看其中一个结果的详细信息页面,显示用于测量它的测试序列。 table 中的每个数字也是 link。这些详细信息页面会告诉您哪个操作数是哪个,并分解从每个输入到每个输出的延迟。让我们 look at zero-masked vmovdqa64 512 位负载(VMOVDQA64_Z (ZMM, K, M512)),他们在 asm 中使用 vmovdqa64 zmm0{k1}{z},ZMMWORD PTR [r14] 进行了测试。列出的延迟为 [1;≤9].

他们将操作数编号为

  • 1 (write-only): ZMM 目的地。
  • 2 (read-only): k0..7 掩码寄存器
  • 3 (read-only): 内存(稍后分解为地址与实际内存内容)

1周期延迟部分是从掩码寄存器到结果的延迟,“延迟操作数 2 → 1:1”。因此,在加载单元获取数据之前,掩码不必准备就绪。

<=9 是从地址基址或索引寄存器到准备好最终 ZMM 结果的延迟。

显然是 store/reload 情况,在 store-forwarding 延迟上出现瓶颈,“延迟操作数 3 → 1(内存):≤6”。他们用这个序列进行了测试,描述为“链延迟:≥6”。 vshufpd zmm 已知有 1 个周期的延迟,我猜他们只是将商店算作有 1 个周期的延迟?就像我说的,他们只是假设一切都是 1 个周期,尽管将任何延迟分配给商店有点可疑。

Code:
   0:   62 d1 fd c9 6f 06       vmovdqa64 zmm0{k1}{z},ZMMWORD PTR [r14]
   6:   62 71 fd 48 c6 e8 00    vshufpd zmm13,zmm0,zmm0,0x0
   d:   62 51 95 48 c6 ed 00    vshufpd zmm13,zmm13,zmm13,0x0
  14:   62 51 95 48 c6 ed 00    vshufpd zmm13,zmm13,zmm13,0x0
  1b:   62 51 95 48 c6 ed 00    vshufpd zmm13,zmm13,zmm13,0x0
  22:   62 51 95 48 c6 ed 00    vshufpd zmm13,zmm13,zmm13,0x0
  29:   62 51 fd 48 11 2e       vmovupd ZMMWORD PTR [r14],zmm13

(对于吞吐量测试,他们多次重复该块以创建一个展开的循环。但对于延迟测试,他们可能只是围绕它包裹一个正常的循环。nanobench 是 open-source 所以你可以检查。)

对于“延迟操作数 3 → 1(地址,基址寄存器):≤9”测量,他们说“链延迟:≥5”。我们知道 vmovq r,x / vmovq x,r round-trip 是超过 2 个周期的延迟,所以这里链的 vmovq 部分可能超过一个周期。这就是为什么他们 over-estimate load-use 延迟,保守上限为 9 个周期。

   0:   62 d1 fd c9 6f 06       vmovdqa64 zmm0{k1}{z},ZMMWORD PTR [r14]
   6:   c4 c1 f9 7e c4          vmovq  r12,xmm0
   b:   4d 31 e6                xor    r14,r12
   e:   4d 31 e6                xor    r14,r12
  11:   4d 31 e6                xor    r14,r12
  14:   4d 31 e6                xor    r14,r12

他们测量:

  • Instructions retired: 6.0
  • Core cycles: 14.0
  • Reference cycles: 10.81
  • UOPS_EXECUTED.THREAD: 7.0

每次迭代总共 14 个周期,因此他们计算出 14-5 = 9 个周期被屏蔽负载所占。 (如果链延迟实际上长于 5,则更少。vmovq 实际上可能是 3 或 4 个周期,因此 7 或 6 个周期的 SIMD 加载延迟听起来是正确的。我们知道整数 load-use 延迟是 5 个周期, 和 IIRC Intel 的优化手册说 SIMD 负载是 6 或 7 个周期. 但是这个保守的上限 9 是我们真正可以说的 sure 纯粹基于测量, 没有外推 /猜测。)


AVX-512指令命名。

“A64”是AVX-512vmovdqa64指令助记符的一部分,当然:查Intel的asm手册:https://www.felixcloutier.com/x86/movdqa:vmovdqa32:vmovdqa64。请记住,AVX-512 在(几乎)每条指令上都支持 per-element 合并或 zero-masking,因此即使 movdqa 和按位运算也需要元素大小。这也是为什么 AVX-512 按位布尔值是 vpord / q 而不是 vpor 的原因(他们本可以使用 b/w/d/q 命名 movdqa 元素大小,但我们有vmovdqadvmovdqaq,但我想我们应该庆幸他们没有。)

幸运的是,a32 与 a64 没有任何性能差异,只有当您使用遮罩时,结果才会有任何差异,例如via _mm512_maskz_load_epi32( __mmask16 k, void * sa) vs. epi64 只需要 __mmask8。或者对于较小的矢量宽度,仅使用少于 8 位的掩码。

Zero-Masking 与合并掩码

op Z (ZMM, K, ZMM)op (ZMM, K, ZMM) 是 zero-masking 与 merge-masking。如果哟不知道 AVX-512 掩蔽是如何工作的,请去阅读它。例如Kirill Yukhin 的演示幻灯片有一个概述: https://en.wikichip.org/w/images/d/d5/Intel_Advanced_Vector_Extensions_2015-2016_Support_in_GNU_Compiler_Collection.pdf

Reg-reg 没有屏蔽的 vmovdqa(没有 k 寄存器)可以是 0 延迟(mov-elimination),但是有屏蔽它总是 1.

有趣的事实:register-renaming for k0..k7 使用与 MMX/x87 相同的物理寄存器文件 space:https://travisdowns.github.io/blog/2020/05/26/kreg2.html