是否有更好的AVX指令来从3个ymm寄存器中移动数据



我有三个ymm寄存器——ymm4、ymm5和ymm6——用双精度(qword(浮点进行封装:

ymm4:   73  144 168 41
ymm5:   144 348 26  144
ymm6:   732 83  144 852

我想写上面矩阵的每一列。例如:

-- extract ymm4[63:0] and insert it at ymm0[63:0]
-- extract ymm5[63:0] and insert it at ymm0[127:64]
-- extract ymm6[63:0] and insert it at ymm0[191:128]

使得ymm0读取73144732。

到目前为止,我使用过:

mov rax,4
kmovq k6,rax
vpxor ymm1,ymm1
VEXPANDPD ymm1{k6}{z},ymm6

这导致ymm1读取[0 0 732],所以我完成了第一步,因为732是ymm6中[63:0]处的元素。

对于ymm4和ymm5,我使用vblendpd:

vblendpd ymm0,ymm1,ymm4,1

这导致ymm0读取[73 0 732],所以我完成了第二步,因为73是ymm4中[63:0]处的元素。

现在我需要把ymm5[63:0]放在ymm0[127:64]:

vblendpd ymm0,ymm0,ymm5,2

这导致ymm0读取[73 144 732],所以现在我完成了第一列[63:0]。

但现在我需要对ymm寄存器中的第2、3和4列执行同样的操作。在我添加更多说明之前,这是执行我所描述的操作的最有效方法吗?还有其他更有效的方法吗?

我已经调查了unpackhpd(https://www.felixcloutier.com/x86/unpckhpd),vblendpd(https://www.felixcloutier.com/x86/blendpd,和vshufpd(https://www.felixcloutier.com/x86/shufpd),我上面显示的似乎是最好的解决方案,但它有很多指令,文档中显示的imm8值的编码有些不透明。有没有更好的方法来提取三个ymm寄存器的相应列?

让我们这样命名矩阵元素:

YMM0 = [A,B,C,D]
YMM1 = [E,F,G,H]
YMM2 = [I,J,K,L]

最终,您想要这样的结果,其中*表示"不在乎">

YMM0 = [A,E,I,*]
YMM1 = [B,F,J,*]
YMM2 = [C,G,K,*]
YMM3 = [D,H,K,*]

为了实现这一点,我们将矩阵扩展到4×4(想象另一行只有[*,*,*,*](,然后转置矩阵。这分为两个步骤:首先,每个2×2子矩阵都被转置。然后,交换左上角和右下角矩阵:

[A,B,C,D]       [A,E,C,G]       [A,E,I,*]
[E,F,G,H]  --  [B,F,D,H]  --  [B,F,J,*]
[I,J,K,L]  --/  [I,*,K,*]  --/  [C,G,K,*]
[*,*,*,*]       [J,*,L,*]       [D,H,L,*]

对于ymm0ymm1中的第一步,我们使用一对解压缩指令:

vunpcklpd %ymm1, %ymm0, %ymm4         // YMM4 = [A,E,C,G]
vunpckhpd %ymm1, %ymm0, %ymm5         // YMM5 = [B,F,D,H]

第3行暂时保留在ymm2中,因为它不需要更改。第4行是通过将ymm2本身拆包获得的:

vunpckhpd %ymm2, %ymm2, %ymm6         // YMM5 = [J,*,L,*]

第二步是通过两次混合和交换车道来实现:

vblendpd $0xa, %ymm2, %ymm4, %ymm0    // YMM0 = [A,E,I,*]
vblendpd $0xa, %ymm6, %ymm5, %ymm1    // YMM1 = [B,F,J,*]
vperm2f128 $0x31, %ymm2, %ymm4, %ymm2 // YMM2 = [C,G,K,*]
vperm2f128 $0x31, %ymm6, %ymm5, %ymm3 // YMM3 = [D,H,L,*]

这在7个指令中实现了所需的排列。

请注意,由于这些指令都不需要AVX2,因此此代码将在只有AVX的SandyBridge处理器上运行。

最新更新