从两个 128 位块中收集四个 32 位字
Gather four 32-bit words from two 128-bit blocks
我正在使用 C 内在函数开发从 SSE 到 NEON 的端口。我有两个由 32 位字组成的 128 位块:
[A1 A2 A3 A4] [B1 B2 B3 B4]
我需要像这样将它们收集成两个 uint32x4_t
:
[A1 B1 A2 B2] [A3 B3 A4 B4]
128 位块及其相关步幅给我带来了麻烦。我查看了 ARM 的 NEON Part 1: Load and Stores,但我没有看到跨越两个 16 字节块的内容。
如何从两个 128 位块中收集数据?
VZIP.32
正是您要找的
from MSB to LSB:
q0: A4 | A3 | A2 | A1
q1: B4 | B3 | B2 | B1
vzip.32 q0, q1
q0: B2 | A2 | B1 | A1
q1: B4 | A4 | B3 | A3
在 aarch64
上,情况完全不同。
from MSB to LSB:
v0: A4 | A3 | A2 | A1
v1: B4 | B3 | B2 | B1
zip2 v2.4s, v0.4s, v1.4s
zip1 v3.4s, v0.4s, v1.4s
v2: B2 | A2 | B1 | A1
v3: B4 | A4 | B3 | A3
而且您不应该将时间浪费在内部函数上。
我的汇编版本 4x4 矩阵乘法(浮点数、复数)运行速度几乎是我的 "spoon-fed" 内部函数版本的三倍,由 Clang 编译。
*GCC (7.1.1) 编译版本比 Clang 对应版本稍快,但相差不大。
以下是使用 32 位整数作为示例的内部函数版本。它适用于 A-32 NEON、Aarch32 和 Aarch64。
uint32x4_t vecA, vecB;
...
uint32x4x2_t vecR = vzipq_u32(vecA, vecB);
uint32x4_t vecX = vecR.val[0];
uint32x4_t vecY = vecR.val[1];
请注意,vzip2
结合了前(下)一半,而 vzip1
结合了后(上)一半。它们由 uint32x4x2_t
和 val[0]
和 val[1]
访问。一旦访问 val[]
,编译器就可以 select zip1
和 zip2
指令。
我正在使用 C 内在函数开发从 SSE 到 NEON 的端口。我有两个由 32 位字组成的 128 位块:
[A1 A2 A3 A4] [B1 B2 B3 B4]
我需要像这样将它们收集成两个 uint32x4_t
:
[A1 B1 A2 B2] [A3 B3 A4 B4]
128 位块及其相关步幅给我带来了麻烦。我查看了 ARM 的 NEON Part 1: Load and Stores,但我没有看到跨越两个 16 字节块的内容。
如何从两个 128 位块中收集数据?
VZIP.32
正是您要找的
from MSB to LSB:
q0: A4 | A3 | A2 | A1
q1: B4 | B3 | B2 | B1
vzip.32 q0, q1
q0: B2 | A2 | B1 | A1
q1: B4 | A4 | B3 | A3
在 aarch64
上,情况完全不同。
from MSB to LSB:
v0: A4 | A3 | A2 | A1
v1: B4 | B3 | B2 | B1
zip2 v2.4s, v0.4s, v1.4s
zip1 v3.4s, v0.4s, v1.4s
v2: B2 | A2 | B1 | A1
v3: B4 | A4 | B3 | A3
而且您不应该将时间浪费在内部函数上。
我的汇编版本 4x4 矩阵乘法(浮点数、复数)运行速度几乎是我的 "spoon-fed" 内部函数版本的三倍,由 Clang 编译。
*GCC (7.1.1) 编译版本比 Clang 对应版本稍快,但相差不大。
以下是使用 32 位整数作为示例的内部函数版本。它适用于 A-32 NEON、Aarch32 和 Aarch64。
uint32x4_t vecA, vecB;
...
uint32x4x2_t vecR = vzipq_u32(vecA, vecB);
uint32x4_t vecX = vecR.val[0];
uint32x4_t vecY = vecR.val[1];
请注意,vzip2
结合了前(下)一半,而 vzip1
结合了后(上)一半。它们由 uint32x4x2_t
和 val[0]
和 val[1]
访问。一旦访问 val[]
,编译器就可以 select zip1
和 zip2
指令。