我的想法是,我想将double的返回值收集到向量寄存器中,以便一次对计算机imm width进行处理,而无需先将其存储回内存中。

特殊的处理是一个vfma,其他两个操作数均为constexpr,因此可以简单地将它们用_mm256_setr_pd调用,或将constexpr array中的对齐/未对齐内存加载。

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

目标机器是Kaby Lake。也欢迎将来的矢量指令更有效。

最佳答案

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

double的位模式放入RAX通常也没有用,而且闻起来好像您已经走错了路,进入了次优的领域。通常使用矢量随机播放指令更好:按元素插入/提取指令已经在Intel硬件上花费了随机播放uop的费用,除了vmovq %xmm0, %rax以获得低位元素。

另外,如果要将其插入另一个向量,则应混洗/立即混合。 (vpermpd / vblendpd)。

L1d和存储转发缓存非常快,甚至存储转发停顿也不是灾难。在ALU与内存之间进行明智选择,以将数据收集到SIMD向量中或从中分散出来。还要记住,插入/提取指令需要立即索引,因此,如果您有向量的运行时索引,则肯定要存储它和索引。 (请参阅https://agner.org/optimize/https://stackoverflow.com/tags/x86/info中的其他性能链接)

许多插入/提取操作会很快在Haswell及更高版本的端口5上造成瓶颈。有关更多详细信息,请参见Loading an xmm from GP regs,还有一些指向gcc错误报告的链接,在这些链接中,我更详细地介绍了在不同uarch上以及使用SSE4.1与不使用SSE4.1等情况下针对不同元素宽度的策略。



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

要读取/写入YMM的低通道,必须使用整数vpextrq $1, %xmm0, %rax / pinsrq $1, %rax, %xmm0。这些在YMM宽度中不可用,因此您需要多个指令来读取/写入高通道。

VEX版本vpinsrq $1, %rax, %xmm0将目标向量的完整YMM或ZMM宽度的高通道归零,这就是我建议使用pinsrq的原因。在Skylake及更高版本上,它不会导致SSE / AVX过渡停止。有关示例(NASM语法),请参见Using ymm registers as a "memory-like" storage location,另请参见Loading an xmm from GP regs

对于低元素,请使用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一起用于字节元素(至少具有一个通道)。对于车道交叉,可以使用字节索引的高位混洗+ vmovd,然后使用索引的低位作为字内字节偏移来进行标量右移。另请参见How to use _mm_extract_epi8 function?以获取pextrb的可变索引仿真的内在函数。



YMM的高车道,带AVX2

我认为,最好的选择是使用AVX2在YMM的高通道中写入元素需要暂存器:


vmovq %rax, %xmm0(复制到暂存向量)
vinsertf128(AVX1)或vpbroadcastq / vbroadcastsd随机调整到适当的位置。它比AMD上的vpermq / vpermpd更快。 (但是reg-reg版本仍然仅是AVX2)
vblendpd(FP)或vpblendd(整数)到目标YMM reg中。带有dword或更大元素宽度的立即混合非常便宜(对于Intel上的任何矢量ALU端口,均为1 uop)。


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

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

要读取第二高的元素,vextractf128 $1, %ymm1, %xmm0(AVX1)和vmovq。在AMD CPU上,vextractf128vpermq/pd快。



避免插入时产生刮擦的坏选择是vpermqvperm2i128将要替换的qword混洗到低位字符pinsrq(不是vpinsrq),然后vpermq放回以正确的顺序。全部都是随机码,而pinsrq是2码。 (并导致Haswell上的SSE / AVX过渡停顿,但Skylake上没有)。另外,所有这些操作都是要修改的寄存器的依赖项链的一部分,这与在另一个寄存器中设置值并进行混合不同。

10-08 20:03