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

标签 assembly x86-64 nasm avx avx2

我有三个 ymm 寄存器 - ymm4、ymm5 和 ymm6 - 包含 double (qword) float :

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 和 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 的 Sandy Bridge 处理器上运行。

关于assembly - 是否有更好的 AVX 指令来从 3 ymm 寄存器移动数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63782703/

相关文章:

c - ARM - 如何从 C 函数中执行单个汇编指令(机器代码)

assembly - 为什么我无法转换应用程序中的架构?

c++ - 具有非原子大小项目的无锁双端队列

assembly - 用更少的指令对 64 位寄存器中的所有字节进行异或

assembly - 为什么 x86 int 寄存器的数量是 8 个?

c++ - Visual Studio 中的指针和发布版本

android - IBM Mobile First - Json Store 无法在 Samsung Galaxy S6 上运行

c - 进程 fork 后会发生什么?

assembly - 第二个扇区无法加载到引导文件中

linux - 不执行任何操作的简单 _start 末尾的段错误