TL:DR: 编译-march=skylake-avx512
让编译器使用 EVEX 前缀来访问 ymm16-31,这样它就可以(希望)为具有 17 的代码制作更好的 asm__m256
价值观立刻“活”起来。
-march=skylake-avx512
包括-mavx512vl
例如,skylake 架构有 1 或 2 个 AVX-512 fma 单元。这个数字是否也会改变可用寄存器总数?
不会,所有 Skylake CPU 中的物理寄存器文件大小相同,无论存在多少个 FMA 执行单元。这些东西是完全正交的。
64 位 AVX2 的架构 YMM 寄存器数量为 16 个,64 位 AVX512VL 的架构 YMM 寄存器数量为 32 个。在 32 位代码中,即使使用 AVX512,也始终只有 8 个可用向量寄存器。 (因此对于大多数高性能计算而言,32 位已经非常过时了。)
The longer EVEX encoding required for YMM16-31 with AVX512VL1 + AVX2, but instructions with all operands in the low 16 can use the shorter VEX prefix AVX/AVX2 form of the instruction. (There's no penalty for mixing VEX and EVEX encodings, so VEX is preferable for code-size. But if you avoid y/zmm0-y/zmm15, you don't need VZEROUPPER; legacy-SSE instructions can't touch xmm16-31 so there's no possible problem.)
同样,这一切与存在的 FMA 执行单元的数量无关。
脚注1:
AVX512F仅包含大部分指令的ZMM版本;大多数 YMM 指令的 EVEX 编码需要 AVX512VL。唯一具有 AVX512F 但不具有 AVX512VL 的 CPU 是 Xeon Phi、KNL / KNM,现已停产;所有主流 CPU 均支持其支持的所有 AVX512 指令的 xmm/ymm 版本。
如果我编写一个 nbody 算法,每个 body-body 计算使用 17 个寄存器,第 17 个寄存器可以间接映射(寄存器重命名硬件)
不,这不是 CPU 和机器代码的工作方式。在机器代码中,只有 4 位(不使用仅 AVX512 编码)或 5 位(使用 AVX512 编码)字段来指定指令的寄存器操作数。
如果您的代码需要同时“激活”17 个向量值,则编译器在针对 x86-64 AVX2 时必须发出指令来溢出/重新加载其中一个,这建筑上的只有 16 个 YMM 寄存器。即它有 16 个不同的名称,CPU 可以将其重命名到其更大的内部寄存器文件中。
如果寄存器重命名解决了整个问题,x86-64 就不必费心将架构寄存器的数量从 8 个整数/8 xmm 增加到 16 个整数/16 xmm。
这就是为什么 AVX512 花费了 3 个额外位(dst、src1 和 src2 各 1 位)来允许访问超出 VEX 前缀可编码范围的 32 个架构向量寄存器。 (仅在 64 位模式下;32 位模式仍然只有 8 个。在 32 位模式下,VEX 和 EVEX 前缀是现有指令的无效编码,翻转这些额外的寄存器编号位将使它们解码为valid这些旧指令的编码而不是作为前缀。)
寄存器重命名允许reuse相同架构寄存器的不同值,没有任何错误的依赖关系。即它避免 WAR 和 WAW 危险;它是使无序执行工作的“魔法”的一部分。在考虑 ILP 和乱序执行时,它有助于保持更多价值,但它doesn't帮助您在简单的程序执行顺序的任何时候在架构寄存器中获得更多值。
例如,下面的循环只需要 3 个架构寄存器,并且每次迭代都是独立的(除了指针增量之外,没有循环携带的依赖项)。
.loop:
vaddps ymm0, ymm1, [rsi] ; ymm0 = ymm1, [src]
vmulps ymm0, ymm0, ymm2 ; ymm0 *= ymm2
vmovaps [rsi+rdx], ymm0 ; dst = src + (dst_start - src_start). Stays micro-fused on Haswell+
add rsi, 32
cmp rsi, rcx ; }while(rsi < end_src)
jb .loop
但是,由于从 ymm0 的第一次写入到迭代中的最后一次读取有 8 个周期的延迟链(Skylake addps / mulps 各为 4 个周期),在没有寄存器重命名的 CPU 上,这将成为瓶颈。下一次迭代无法写入 ymm0,直到vmovaps
在本次迭代中读取了该值。
但在乱序 CPU 上,多个迭代会同时进行,每次写入 ymm0 都会被重命名以写入不同的物理寄存器。忽略前端瓶颈(假设我们已展开),CPU 可以使用大约 8 个物理寄存器,保持足够的迭代运行,以每个时钟 2 个 addps/mulps 微指令使 FMA 单元饱和。 (或者更多,因为它们实际上不能在退休之前被释放,而不仅仅是在最后一个微指令读取该值后立即释放)。
有限的物理寄存器文件大小可以是无序窗口大小的限制,而不是 ROB 或调度程序大小.
(我们考虑了一段时间,Skylake-AVX512 对 ZMM 寄存器使用 2 个 PRF 条目,基于这个结果,但后来更详细的实验表明,AVX512模式会启动更宽的PRF,或者说上通道来补充现有的PRF,因此AVX512模式下的SKX仍然具有与256位物理寄存器相同数量的512位物理寄存器。看@BeeOnRope 和 @Mysticial 之间的讨论。我认为某处有更好的实验+结果记录,但我在 ATM 机上找不到。)
有关的:为什么mulss在Haswell上只需要3个周期,与Agner的指令表不同? (使用多个累加器展开 FP 循环)(答案:不是;OP对寄存器重用感到困惑。我的答案详细解释了很多细节,并进行了一些有趣的多个向量累加器的性能实验。)