如何将AVX512寄存器zmm26中的QuadWord写入rax寄存器?
我希望对zmm 0-31寄存器组的四字单元执行整数算术运算,并保留这些运算产生的进位位。 看来这只有在数据在通用寄存器组中进行处理时才有可能。
因此,我想将信息从zmm 0-31寄存器中的一个复制到通用寄存器之一。 在处理通用寄存器中的64位数据后,我想将数据返回到来自同一个QuadWord位置的原始zmm 0-31寄存器。 我知道我可以使用命令将数据从通用寄存器rax移动到AVX512寄存器zmm26 QuadWord位置5
vpbroadcastq zmm26{k5}{z},rax
其中8位掩码k5 =十进制32,允许将数据广播到zmm26的第5个四字节并且z = 1,表示zmm26中没有其他QWord受到影响,并且rax是数据的来源。
但是我找不到一个反向命令,它会将来自寄存器zmm26,Quad 5的数据写入rax寄存器。 看来,我只能使用vmovq rax,xmm1命令将最低有效的QuadWord从AVX寄存器复制到通用寄存器。 并且没有使用屏蔽zmm 0-31来源的广播命令。
我很想知道我的命令选项是从zmm 0-31寄存器到rax寄存器的某个特定的QuadWord是什么。 此外,除了英特尔手册之外,还有关于AVX512指令集的其他描述性信息来源吗?
与一些较早的SIMD扩展有“提取”指令,如pextrq
可直接执行此操作,我不知道有什么方法可以在AVX-512(或带有ymm寄存器的AVX)中进行:
将你想要的元素置换/混洗成低位四字,然后使用你所指出的vmovq
把它放到一个通用寄存器中。
将整个矢量存储到临时内存位置loc
(例如堆栈),然后使用mov register,[loc + offset]
指令读取您感兴趣的任何qword(s)。
这两种方法看起来相当丑陋,哪一种更好取决于你的确切场景。 尽管使用内存作为中介,但如果您计划从每个向量提取多个值,则第二种方法可能会更快,因为您可以在最近的CPU上使用具有一个加载/周期吞吐量的加载端口,而排列/混排方法可能会阻碍排列/洗牌所需的端口。
请参阅Peter的回答以获得更全面的处理,包括将vcompress
指令与面罩一起用作一种穷人的提取物。
vpbroadcastq zmm26{k5}{z},rax
是一个有趣的黑客; 如果它能够有效运行,它可能会有用 尤其是使用merge-masking作为vmovq
/ vpinsrq
的替代方案。
除了元素0或1之外,没有使用vpbroadcastq
单指令反转: vmovq rax, xmm26
或vpextrq rax, xmm26, 1
。 是的,这些指令的EVEX编码分别允许他们访问xmm16-31,AVX512F和AVX512DQ。 如果您的数据在xmm0-15中,则可以使用较短的VEX编码版本。
但是, 您可能会滥用VPCOMPRESSQ zmm1/m512 {k5}{z}, zmm26
以使用您用于vpbroadcast
的相同单组位掩码寄存器来执行您想要的内存或zmm目标vpbroadcast
。 但它并不像其他选项那么快,所以唯一的优势是使用相同的掩码寄存器作为随机播放控制,如果无法将循环启动,则可以节省工作量。
在KNL上, VPCOMPRESSQ
(带有寄存器目的地)每3个周期有一个吞吐量(根据Agner Fog的测试)。 在Skylake-AVX512上,每2个周期一个,延迟3c。 这两个CPU都以每个周期1次的速度运行vpermq
,因此可能会对其他指令造成较小的干扰。 我还没有找到内存目标版本的vpcompressq
。
在没有存储/重载的情况下转向另一个方向需要至少一个shuffle uop和一个单独的uop从向量复制到GP寄存器(如vmovq
)。 (如果你最终需要所有的元素,一个存储/重载可能比纯粹的ALU策略更好,ALU的第一个或第二个可能是好的,所以你可以让它们具有低延迟,这样一些依赖操作就可以开始)。
如果你的值在128b“通道”的低64b中 (即偶数编号的元素),那么vextracti64x2 xmm1, zmm26, 3
/ vmovq rax, xmm1
对于单个元素的效率尽可能高。 奇怪的名字是因为AVX512版本的vextracti128
有两种掩盖粒度。 如果您想要的元素位于zmm0-15的第二个128b通道中,则可以使用vextracti128 xmm1, ymm6, 1
(AVX2指令仅使用3字节VEX前缀,而不是4字节EVEX)保存代码大小。
但是如果你的价值在一条通道的上部64b(即一个奇数元素,从0开始计数),你需要vpextrq rax, xmm, 1
而不是vmovq
,并且它解码(在Skylake上)到一个洗牌vmovq
和vmovq
。 (切勿使用vpextrq rax, xmm, 0
为什么编译器优化,因为它浪费了洗牌UOP,这是_mm_extract_epi64(v, 0)
到vmovq
。)
对于奇数编号的元素,您仍然可以在vpermq zmm1, zmm2, zmm3/m512/m64bcst
+ vmovq
中进行一次洗牌 。 如果您需要循环提取,请在循环外部设置一个随机向量常量。 或者,如果您仍然需要其他常量(因此您的函数已经有一个常量的高速缓存行),则广播加载内存操作数应该没问题,如果不在循环中。
当索引不是一个编译时常量时 , vpermq
+ vmovq
也可以工作 ,因为你在一个shuffle控制向量中需要的只是索引在元素0中。例如vmovd xmm7, ecx
会为vpermq zmm1, zmm2, zmm7
/ vmovq rax, zxm1
正如@Bee所说,如果您需要多个元素,存储/重新加载是一个不错的选择。 如果您需要运行时变量元素,您也可以使用它,因为从对齐的512b商店到对齐的64b重新加载的存储转发可能不会停顿。 (比vpermq
解决方案的延迟时间vpermq
,但只使用内存vpermq
,而不是ALU。在Skylake-AVX512中,ALU uops可能非常重要,其中port1在运行512b uops时不会运行任何向量uops。)
如果您的元素编号是编译时常量,则可以使用vextracti64x2 [rsp-16], zmm26, 3
将ZMM向量的所需128b通道存储到内存中。 (或者vextracti128
如果它是通道1的话)。如果你最终想要内存中的值,你可以使用掩码寄存器,只设置第二位来存储高位元素。 (但是IDK的性能表现如何,如果额外的蒙版部分进入一个未映射的页面IIRC,它并没有真正的错误,但是微体系结构上它的处理速度可能会很慢,即使在128b满宽度可能会变慢。)
AVX2 VEXTRACTI128 [mem], ymm, 1
指令在Skylake上运行,只是一个(非微型融合)存储,没有随机端口(http://agner.org/optimize/)。 AVX512提取到内存有希望是相同的,仍然没有使用随机播放器。 (吞吐量/延迟Instlatx64数据可用,但我们不知道什么与哪些吞吐量资源竞争,因此它比Agner Fog的指令表有用得多。)
对于KNL, VEXTRACTF32X4 [mem], zmm
是4 VEXTRACTF32X4 [mem], zmm
,吞吐量很差,而AVX2 vextracti128 [mem], ymm, imm8
是相同的。 所以(假设存储转发工作正常)只需将整个512b矢量存储在KNL上。
上一篇: How can I write a QuadWord from AVX512 register zmm26 to the rax register?