使用VBROADCASTS命令在AVX中很容易,如果值是双精度或浮点数,则在SSE中很容易。
如何向Delphi ASM中的XMM寄存器中的每个插槽广播一个8位值?
答案 0 :(得分:4)
你的意思是你在XMM寄存器的LSB中有一个字节,并希望在该寄存器的所有通道中复制它?我不知道Delphi的内联汇编语法,但在Intel / MASM语法中,可以这样做:
punpcklbw xmm0,xmm0 ; xxxxxxxxABCDEFGH -> xxxxxxxxEEFFGGHH
punpcklwd xmm0,xmm0 ; xxxxxxxxEEFFGGHH -> xxxxxxxxGGGGHHHH
punpckldq xmm0,xmm0 ; xxxxxxxxGGGGHHHH -> xxxxxxxxHHHHHHHH
punpcklqdq xmm0,xmm0 ; xxxxxxxxHHHHHHHH -> HHHHHHHHHHHHHHHH
答案 1 :(得分:3)
最快的选项是pshufb
的SSSE3(如果可用)。
; SSSE3
pshufb xmm0, xmm1 ; where xmm1 is zeroed, e.g. with pxor xmm1,xmm1
否则你通常应该使用它:
; SSE2 only
punpcklbw xmm0, xmm0 ; xxxxxxxxABCDEFGH -> xxxxxxxxEEFFGGHH
pshuflw xmm0, xmm0, 0 ; xxxxxxxxEEFFGGHH -> xxxxxxxxHHHHHHHH
punpcklqdq xmm0, xmm0 ; xxxxxxxxHHHHHHHH -> HHHHHHHHHHHHHHHH
这比punpckl bw / wd更好 - > pshufd xmm0, xmm0, 0
,因为有some CPUs with only 64-bit shuffle units. (Including Merom and K8)。在这样的CPU上,pshuflw
速度很快,punpcklqdq
也是如此,但pshufd
和punpck
的粒度小于64位的速度很慢。所以这个序列只使用一个"慢速洗牌"指令,bw / wd / pshufd为3。
在所有后来的CPU上,这两个3指令序列之间没有区别,因此在这种情况下调整旧CPU的成本并不高。有关指令表,另请参阅http://agner.org/optimize/。
这是迈克尔回答的序列,中间两条指令被pshuflw
取代。
如果您的字节在整数寄存器中,则可以使用乘以0x01010101
将其广播为4个字节。 e.g。
; movzx eax, whatever
imul edx, eax, 0x01010101 ; edx = al repeated 4 times
movd xmm0, eax
pshufd xmm0, xmm0, 0
请注意,imul
的非直接源操作数可以是内存,但它必须是32位内存位置,并且您的字节零扩展为32位。
如果您的数据在内存中启动,首先加载到整数寄存器可能不值得。只需movd
到xmm寄存器。 (或者可能pinsrb
如果你需要避免更宽的负载以避免跨越页面或者可能是高速缓存行。但是这对于movd
没有pmuludq
的寄存器的旧值具有错误的依赖性。吨。)
如果指令吞吐量比延迟更重要,那么如果您不能使用pshufb
,则值得考虑; low 32 bits of xmm0 = your byte, **zero extended**
pmuludq xmm0, xmm7 ; xmm7 = 0x01010101 in the low 32 bits
pshufd xmm0, xmm0, 0
,即使它在大多数CPU上有5个周期延迟。 / p>
-2147483648