如何将 %rax 中的双精度移动到 %ymm 或 %zmm 上的特定 qword 位置? (卡比湖或更高版本)

How to move double in %rax into particular qword position on %ymm or %zmm? (Kaby Lake or later)

我的想法是,我想将 double 的返回值一次 收集到一个向量寄存器中,以供机器 imm width 处理先存储回内存。

特定的处理是一个vfma,另外两个操作数都是constexpr,所以它们可以简单地被_mm256_setr_pd或aligned/unaligned内存加载调用constexpr array.

有没有一种方法可以直接从 %rax 中的值将 double 存储在 %ymm 中的特定位置以用于收集目的?

目标机器是Kaby Lake。也欢迎未来更高效的向量指令。

内联汇编通常不是一个好主意:现代编译器在 x86 内部函数方面做得很好。

double 的位模式放入 RAX 通常也没有用,而且听起来你可能已经走错了路,进入了次优领域。矢量洗牌指令通常更好:按元素 insert/extract 指令已经在英特尔硬件上花费了洗牌 uop,除了 vmovq %xmm0, %rax 以获得低元素。

此外,如果您要将其插入到另一个向量中,您应该 shuffle/immediate-blend。 (vpermpd / vblendpd).

L1d 和 store-forwarding 缓存很快,甚至 store-forwarding 停顿也不是灾难。在 ALU 与内存之间明智地选择以收集数据或将数据分散到 SIMD 向量中或从中分散数据。还要记住 insert/extract 指令需要一个立即索引,所以如果你有一个向量的运行时索引,你肯定想存储它并索引。 (参见 https://agner.org/optimize/ and other performance links in https://whosebug.com/tags/x86/info

大量 insert/extract 会很快在 Haswell 及更高版本的端口 5 上造成瓶颈。 有关更多详细信息,请参阅 ,以及指向 gcc 错误报告的一些链接,其中我详细介绍了不同 uarche 上不同元素宽度的策略以及使用 SSE4.1 与不使用 SSE4.1 的策略SSE4.1等


extractps r/m32, xmm, imm 没有 PD 版本,insertps 是 XMM 向量之间的洗牌。

到read/write YMM 的低车道,你必须使用整数vpextrq , %xmm0, %rax / pinsrq , %rax, %xmm0。这些在 YMM 宽度中不可用,因此您需要多条指令才能 read/write 高车道。

VEX 版本 vpinsrq , %rax, %xmm0 会将目标矢量的完整 YMM 或 ZMM 宽度的高车道归零,这就是我建议 pinsrq 的原因。在 Skylake 及更高版本上,它不会导致 SSE/AVX 转换停顿。有关示例(NASM 语法),请参阅 ,以及

对于低元素,使用vmovq %xmm0, %rax提取,它比vpextrq便宜(1 uop而不是2)。


对于 ZMM,我对来自 GP regs 问题的链接 XMM 的回答表明,您可以使用 AVX512F 将整数寄存器合并屏蔽为向量,给定屏蔽寄存器单个位集。

vpbroadcastq %rax, %zmm0{%k1}

同理,vpcompressq可以将单位掩码选中的元素移动到vmovq的底部。

但是对于提取,如果有一个索引而不是 1<<index 开始 ,你可能会更好 vmovd %ecx, %zmm1 /vpermd %zmm0, %zmm1, %zmm2/vmovq %zmm2, %rax。这个技巧甚至适用于 vpshufb 字节元素(至少有一个通道)。对于车道交叉,可能使用字节索引的高位进行 shuffle + vmovd,然后使用索引的低位作为字内字节偏移量进行标量右移。另请参阅 How to use _mm_extract_epi8 function? 以了解 pextrb.

的变量索引模拟的内在函数

YMM 的高速车道,带有 AVX2

我认为使用 AVX2 在 YMM 的高速通道中编写元素的最佳选择需要暂存器:

  • vmovq %rax, %xmm0(复制到一个scratch vector)
  • vinsertf128 (AVX1) 或 vpbroadcastq/vbroadcastsd 洗牌。它比 AMD 上的 vpermq/vpermpd 快。 (但 reg-reg 版本仍然是 AVX2-only)
  • vblendpd(FP)或vpblendd(整数)到目标 YMM reg。与 dword 或更大元素宽度的立即混合非常便宜(Intel 上的任何矢量 ALU 端口为 1 uop)。

这只有 3 个 uops,但其中 2 个需要 Intel CPU 上的端口 5。 (因此它的成本与 vpinsrq + 混合的成本相同)。只有混合在从矢量输入到矢量输出的关键路径上,从rax设置ymm0是独立的。

读取最高元素,vpermpdvpermq , %ymm1, %ymm0(AVX2),然后从xmm0读取vmovq

读取第二高的元素,vextractf128 , %ymm1, %xmm0 (AVX1) 和 vmovqvextractf128 在 AMD CPU 上比 vpermq/pd 快。


避免插入的刮擦 reg 的一个不好的替代方法是 vpermqvperm2i128 将要替换的 qword 洗牌到低通道,pinsrqnot vpinsrq), 然后 vpermq 把它放回正确的顺序。这就是所有的 shuffle 微指令,pinsrq 是 2 微指令。 (并在 Haswell 上导致 SSE/AVX 转换停顿,但 Skylake 不会)。此外,所有这些操作都是您正在修改的寄存器的依赖链的一部分,这与在另一个寄存器中设置值并混合不同。