如何使用 neon intrinsic 在通道中添加所有 int32 元素
How to add all int32 element in a lane using neon intrinsic
这是我在泳道中添加所有 int16x4 元素的代码:
#include <arm_neon.h>
...
int16x4_t acc = vdup_n_s16(1);
int32x2_t acc1;
int64x1_t acc2;
int32_t sum;
acc1 = vpaddl_s16(acc);
acc2 = vpaddl_s32(acc1);
sum = (int)vget_lane_s64(acc2, 0);
printf("%d\n", sum);// 4
并且我尝试在一个泳道中添加所有 int32x4 元素。
但我的代码看起来效率低下:
#include <arm_neon.h>
...
int32x4_t accl = vdupq_n_s32(1);
int64x2_t accl_1;
int64_t temp;
int64_t temp2;
int32_t sum1;
accl_1=vpaddlq_s32(accl);
temp = (int)vgetq_lane_s64(accl_1,0);
temp2 = (int)vgetq_lane_s64(accl_1,1);
sum1=temp+temp2;
printf("%d\n", sum);// 4
有没有简单明了的方法呢?希望编译后的LLVM汇编代码简单明了。我也希望 sum
的最终类型是 32 位。
我使用 ellcc 基于 LLVM 编译器基础架构的交叉编译器来编译它。
我在 Whosebug 上看到了类似的问题 (Add all elements in a lane),但是内在的 addv
在我的主机上不起作用。
如果您只想要 32 位结果,大概不太可能出现中间溢出,或者您根本不关心它,在这种情况下,您可以一直保持 32 位:
int32x2_t temp = vadd_s32(vget_high_s32(accl), vget_low_s32(accl));
int32x2_t temp2 = vpadd_s32(temp, temp);
int32_t sum1 = vget_lane_s32(temp2, 0);
然而,使用 64 位累加实际上并不麻烦,并且也可以在不退出 NEON 的情况下完成 - 只是操作顺序不同:
int64x2_t temp = vpaddlq_s32(accl);
int64x1_t temp2 = vadd_s64(vget_high_s64(temp), vget_low_s64(temp));
int32_t sum1 = vget_lane_s32(temp2, 0);
这些都归结为只有 3 条 NEON 指令,没有标量运算。 32 位 ARM 的关键技巧是 Q 寄存器的两半成对相加只是两个 D 寄存器的正常相加——这不适用于 SIMD 寄存器布局不同的 AArch64,但 AArch64 具有前面提到的水平 addv
无论如何。
现在,我不知道这些在 LLVM IR 中看起来有多可怕——我想这取决于它如何在内部处理向量类型和操作——但就最终的 ARM 机器代码而言,两者都可以被认为是最佳的.
这是我在泳道中添加所有 int16x4 元素的代码:
#include <arm_neon.h>
...
int16x4_t acc = vdup_n_s16(1);
int32x2_t acc1;
int64x1_t acc2;
int32_t sum;
acc1 = vpaddl_s16(acc);
acc2 = vpaddl_s32(acc1);
sum = (int)vget_lane_s64(acc2, 0);
printf("%d\n", sum);// 4
并且我尝试在一个泳道中添加所有 int32x4 元素。
但我的代码看起来效率低下:
#include <arm_neon.h>
...
int32x4_t accl = vdupq_n_s32(1);
int64x2_t accl_1;
int64_t temp;
int64_t temp2;
int32_t sum1;
accl_1=vpaddlq_s32(accl);
temp = (int)vgetq_lane_s64(accl_1,0);
temp2 = (int)vgetq_lane_s64(accl_1,1);
sum1=temp+temp2;
printf("%d\n", sum);// 4
有没有简单明了的方法呢?希望编译后的LLVM汇编代码简单明了。我也希望 sum
的最终类型是 32 位。
我使用 ellcc 基于 LLVM 编译器基础架构的交叉编译器来编译它。
我在 Whosebug 上看到了类似的问题 (Add all elements in a lane),但是内在的 addv
在我的主机上不起作用。
如果您只想要 32 位结果,大概不太可能出现中间溢出,或者您根本不关心它,在这种情况下,您可以一直保持 32 位:
int32x2_t temp = vadd_s32(vget_high_s32(accl), vget_low_s32(accl));
int32x2_t temp2 = vpadd_s32(temp, temp);
int32_t sum1 = vget_lane_s32(temp2, 0);
然而,使用 64 位累加实际上并不麻烦,并且也可以在不退出 NEON 的情况下完成 - 只是操作顺序不同:
int64x2_t temp = vpaddlq_s32(accl);
int64x1_t temp2 = vadd_s64(vget_high_s64(temp), vget_low_s64(temp));
int32_t sum1 = vget_lane_s32(temp2, 0);
这些都归结为只有 3 条 NEON 指令,没有标量运算。 32 位 ARM 的关键技巧是 Q 寄存器的两半成对相加只是两个 D 寄存器的正常相加——这不适用于 SIMD 寄存器布局不同的 AArch64,但 AArch64 具有前面提到的水平 addv
无论如何。
现在,我不知道这些在 LLVM IR 中看起来有多可怕——我想这取决于它如何在内部处理向量类型和操作——但就最终的 ARM 机器代码而言,两者都可以被认为是最佳的.