【问题标题】:Can AVX2-compiled program still use 32 registers of an AVX-512 capable CPU?AVX2 编译的程序是否仍然可以使用支持 AVX-512 的 CPU 的 32 个寄存器?
【发布时间】:2018-07-31 06:09:06
【问题描述】:

假设针对 AVX2 的编译和使用 C++ 内在函数,如果我编写一个 nbody 算法,每次体体计算使用 17 个寄存器,第 17 个寄存器可以间接(寄存器重命名硬件)或直接(Visual Studio 编译器,gcc 编译器)映射在 AVX-512 寄存器上以切断内存依赖性?例如,skylake 架构有 1 或 2 个 AVX-512 fma 单元。这个数字是否也会改变可用的总寄存器? (具体来说,一个至强银 4114 cpu)

如果这行得通,它是如何工作的?当所有指令为 AVX2 或更少时,第一个硬件线程使用每个 ZMM 向量的前半部分,第二个硬件线程使用每个 ZMM 向量的后半部分?


编辑:如果目标机器上会有在线编译(例如使用 OpenCL)怎么办?司机可以为我做以上注册使用吗?

【问题讨论】:

    标签: x86 compiler-optimization cpu-architecture cpu-registers avx512


    【解决方案1】:

    TL:DR:使用 -march=skylake-avx512 编译,让编译器使用 EVEX 前缀访问 ymm16-31,这样它就可以(希望)为具有 17 个 __m256 值的代码生成更好的 asm 一次“活”。

    -march=skylake-avx512 包括-mavx512vl


    例如,skylake 架构有 1 或 2 个 AVX-512 fma 单元。这个数字是否也会改变可用的寄存器总数?

    不,所有 Skylake CPU 中的物理寄存器文件大小相同,无论存在多少 FMA 执行单元。这些东西是完全正交的。

    架构 YMM 寄存器的数量对于 64 位 AVX2 为 16,对于 64 位 AVX512VL 为 32。在 32 位代码中,总是只有 8 个向量寄存器可用,即使是 AVX512。 (所以 32 位对于大多数高性能计算来说已经过时了。)

    使用 AVX512VL1 + AVX2 的 YMM16-31 需要较长的 EVEX 编码,但所有操作数都在低 16 位的指令可以使用较短的 VEX 前缀 AVX/AVX2 形式的指令。 (混合 VEX 和 EVEX 编码没有任何惩罚,因此 VEX 更适合代码大小。但如果避免 y/zmm0-y/zmm15,则不需要 VZEROUPPER;legacy-SSE 指令无法触及 xmm16-31所以没有可能的问题。)

    同样,这与存在的 FMA 执行单元的数量无关。

    脚注 1: AVX512F 只包含大部分指令的 ZMM 版本;大多数 YMM 指令的 EVEX 编码都需要 AVX512VL。仅有 AVX512F 而不是 AVX512VL 的 CPU 是 Xeon Phi、KNL / KNM,现已停产;所有主流 CPU 都支持它们支持的所有 AVX512 指令的 xmm/ymm 版本。

    如果我编写一个 nbody 算法,每次体体计算使用 17 个寄存器,第 17 个寄存器是否可以间接映射(寄存器重命名硬件)

    不,这不是 CPU 和机器代码的工作方式。 在机器代码中,只有一个 4 位(不使用 AVX512-only 编码)或 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 前缀是现有指令的无效编码,翻转那些额外的寄存器编号位会使它们解码为 这些旧指令的有效编码而不是前缀。)


    寄存器重命名允许重复使用相同的架构寄存器以获得不同的值,而不会产生任何错误的依赖性。即它avoids WAR and WAW hazards;它是使乱序执行工作的“魔法”的一部分。在考虑 ILP 和乱序执行时,它有助于保持更多的价值,但它帮助您在简单的程序执行顺序中的任何时候在架构寄存器中获得更多的值。 p>

    比如下面的循环只需要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 上,它会成为瓶颈。直到本次迭代中的vmovaps 读取该值后,下一次迭代才能写入 ymm0。

    但在无序 CPU 上,多个迭代同时进行,每次对 ymm0 的写入重命名为写入不同的物理寄存器。忽略前端瓶颈(假设我们展开),CPU 可以保持足够的迭代进行,以使 FMA 单元饱和,每个时钟使用 2 个 addps/mulps uops,使用大约 8 个物理寄存器。 (或者更多,因为直到退休后才能真正释放它们,而不是在最后一个微指令读取该值后立即释放)。

    有限的物理寄存器文件大小can be the limit on the out-of-order windows size, instead of the ROB or scheduler size

    (基于this result,我们曾一度认为 Skylake-AVX512 使用 2 个 PRF 条目作为 ZMM 寄存器,但后来更详细的实验表明,AVX512 模式可以启动更宽的 PRF 或更高的通道以补充现有PRF,所以 AVX512 模式下的 SKX 仍然具有与 256 位物理寄存器相同数量的 512 位物理寄存器。请参阅discussion between @BeeOnRope and @Mysticial。我认为在某处有更好的实验 + 结果写,但我不能找到它 ATM。)


    相关:Why does mulss take only 3 cycles on Haswell, different from Agner's instruction tables? (Unrolling FP loops with multiple accumulators)(回答:它没有;OP 对寄存器重用感到困惑。我的回答详细解释了许多关于多个向量累加器的有趣性能实验。)

    【讨论】:

    • 一条指令卡住/冻结不会停止整个窗口,对吗?是否存在导致指令长时间无法退出的情况?
    • @huseyintugrulbuyukisik:像缓存未命中加载这样的“卡住”指令确实需要一个大的乱序窗口来隐藏延迟。如果 ROB 充满已执行但未退出的微指令,它就会停止。如果 RS 充满了未执行的微指令(都依赖于缓存未命中负载),它就会停止。这是 CPU 设计中的一个主要问题,因为 CPU 频率相对于内存访问时间变得更高。从长远来看,诸如检查点和允许乱序退出的千指令处理器之类的主要新想法可能是前进的方向。 csl.cornell.edu/~martinez/doc/taco04.pdf
    • 这是我第一次看到“乱序退休”。我以为他们都按照发布的顺序退休(但执行顺序不正确)。或者那是我的无知。谢谢你。我猜 Skylake 是千指令,或者你的意思是每个线程还是问题宽度(其中 skylake 是 4-6-8 宽)?
    • @huseyintugrulbuyukisik:不,阅读我链接的论文。乱序退休/KIP是一个全新的想法; Skylake 确实那样工作; SKL 按顺序退休(与其他所有内容一样)和the ROB size is (only) 224 uops,远不及 1k 指令。 Skylake 有 4 宽。我只提到了 KIP,因为它是一种理论上的 CPU 架构理念,可以让 CPU 在一条指令卡住时不会停止。
    【解决方案2】:

    没有。如果您以 AVX2 架构为目标,则生成的代码必须能够在任何支持 AVX2 的 CPU 上运行。其中许多不支持 AVX-512,因此它们没有您想要使用的额外寄存器。

    话虽如此,您没有理由不能使用 AVX512VL 支持进行编译(即 gcc 中的 -mavx512vl)并使用 AVX2 内部函数编写代码。在这种情况下,编译器将能够使用额外的寄存器,因为它针对的是 AVX-512 架构,所有这些架构都包含 32 个[xyz]mm 寄存器。

    【讨论】:

    • “额外”寄存器已经以重命名寄存器的形式存在了很长一段时间。您只是无法直接访问它们。
    • AVX512F 是不够的:对于大多数指令的 EVEX 编码,您需要 AVX512VL 才能使用 YMM16-31 而不是完整的 ZMM16-31。使用-march=skylake-avx512
    • @PeterCordes 这个问题实际上提出了另一个问题。物理上,有多少个寄存器? Skylake 客户端的幻灯片显示了 168 个“FP”寄存器,这通常意味着向量寄存器。但它没有说它们有多大。带有 AVX512 的 Skylake 服务器与 Skylake 客户端共享相同的核心,但具有外部 L2 和 FMA。
    • @PeterCordes 如果 168 个寄存器是 512 位宽,这意味着所有 Skylake 客户端芯片上都有很多死硅。或者它们可能只有 256 位宽,在 512 位模式下,它们成对组合。有趣的是,我看到了似乎支持这一点的东西。我有一些(仅 FP)代码具有长依赖链,当比较 256 位和 512 位在其他方面相同的序列(和相同的时钟频率)时,512 位的代码要慢得多。而且我认为 6 周期的 port5 延迟不足以解释它。
    • @Mysticial:是的,我想知道这一点。如果每个 PRF 条目大到足以容纳一个 ZMM 寄存器,那么在 Skylake 客户端中就会浪费很多晶体管,其中只有低 256 位可用。用完一对 PRF 条目很有意义,因为 AVX512 是新的且很少使用,并且可以在某种程度上解释为什么 SKX 必须在 512b 操作运行时关闭矢量 ALU 端口。 (如果读取 ZMM 寄存器需要两个寄存器读取端口,则寄存器读取端口会受到限制)。所以你认为使用 ZMM 寄存器的无序窗口大小明显更小?
    猜你喜欢
    • 2019-04-25
    • 2018-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-25
    • 2022-07-09
    相关资源
    最近更新 更多