我的想法是,我想将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读取
vpermpd
或vpermq $3, %ymm1, %ymm0
(AVX2),然后读取vmovq
。要读取第二高的元素,
vextractf128 $1, %ymm1, %xmm0
(AVX1)和vmovq
。在AMD CPU上,vextractf128
比vpermq/pd
快。避免插入时产生刮擦的坏选择是
vpermq
或vperm2i128
将要替换的qword混洗到低位字符pinsrq
(不是vpinsrq
),然后vpermq
放回以正确的顺序。全部都是随机码,而pinsrq
是2码。 (并导致Haswell上的SSE / AVX过渡停顿,但Skylake上没有)。另外,所有这些操作都是要修改的寄存器的依赖项链的一部分,这与在另一个寄存器中设置值并进行混合不同。