是否有更好的 AVX 指令从 3 个 ymm 寄存器移动数据?
Are there better AVX instructions to move data from 3 ymm registers?
我有三个 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 读取 73、144、732。
到目前为止我用过:
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 列执行相同的操作。在我添加更多说明之前,这是执行我所描述的最有效方法吗?还有其他更有效的方法吗?
我调查了 unpckhpd (https://www.felixcloutier.com/x86/unpckhpd), vblendpd (https://www.felixcloutier.com/x86/blendpd, and 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,*]
对于ymm0
和ymm1
中的第一步,我们使用一对解包指令:
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 [=15=]xa, %ymm2, %ymm4, %ymm0 // YMM0 = [A,E,I,*]
vblendpd [=15=]xa, %ymm6, %ymm5, %ymm1 // YMM1 = [B,F,J,*]
vperm2f128 [=15=]x31, %ymm2, %ymm4, %ymm2 // YMM2 = [C,G,K,*]
vperm2f128 [=15=]x31, %ymm6, %ymm5, %ymm3 // YMM3 = [D,H,L,*]
这在 7 条指令中实现了所需的排列。
请注意,由于 none 这些指令需要 AVX2,因此此代码将 运行 在仅具有 AVX 的 Sandy Bridge 处理器上。
我有三个 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 读取 73、144、732。
到目前为止我用过:
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 列执行相同的操作。在我添加更多说明之前,这是执行我所描述的最有效方法吗?还有其他更有效的方法吗?
我调查了 unpckhpd (https://www.felixcloutier.com/x86/unpckhpd), vblendpd (https://www.felixcloutier.com/x86/blendpd, and 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,*]
对于ymm0
和ymm1
中的第一步,我们使用一对解包指令:
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 [=15=]xa, %ymm2, %ymm4, %ymm0 // YMM0 = [A,E,I,*]
vblendpd [=15=]xa, %ymm6, %ymm5, %ymm1 // YMM1 = [B,F,J,*]
vperm2f128 [=15=]x31, %ymm2, %ymm4, %ymm2 // YMM2 = [C,G,K,*]
vperm2f128 [=15=]x31, %ymm6, %ymm5, %ymm3 // YMM3 = [D,H,L,*]
这在 7 条指令中实现了所需的排列。
请注意,由于 none 这些指令需要 AVX2,因此此代码将 运行 在仅具有 AVX 的 Sandy Bridge 处理器上。