有效使用vmlaq_s16

时间:2014-07-15 18:04:35

标签: arm simd neon

使用 vmlaq_s16 内在/ VMLA.I16 指令时,结果采用一组8个 16位整数的形式。但是,指令内的乘法要求将结果存储在 32位整数中以防止溢出。

在具有SSE2的Intel处理器上,_mm_madd_epi16通过乘以和添加向量的连续元素对来保留指令的长度(8个16位整数到4个32位结果),即

r0 := (a0 * b0) + (a1 * b1)
r1 := (a2 * b2) + (a3 * b3)
r2 := (a4 * b4) + (a5 * b5)
r3 := (a6 * b6) + (a7 * b7)

其中r0,r1,r2,r3都是32位,a0-a7,b0-b7都是16位元素。

vmlaq_s16指令是否有一个技巧可以让我仍然能够一次处理8个16位元素并且结果不会溢出?或者这个指令是仅为4位范围内的操作数提供的(非常值得怀疑)?

谢谢!

编辑:所以我只想到如果vmlaq_s16为结果中的每个元素设置溢出寄存器标志(s?),那么它很容易计算溢出并恢复结果。

编辑2 :对于每个人的参考,这里有如何加载8个元素并在具有内在函数的128位寄存器上管道两个长乘法加法(概念证明代码,使用VS2012编译ARM目标):

signed short vector1[] = {1, 2, 3, 4, 5, 6, 7, 8};
signed short vector2[] = {1, 2, 3, 4, 5, 6, 7, 8};

int16x8_t v1; // = vdupq_n_s16(0);
int16x8_t v2; // = vdupq_n_s16(0);

v1 = vld1q_s16(vector1);
v2 = vld1q_s16(vector2);

int32x4_t sum = vdupq_n_s16(0);
sum = vmlal_s16(sum, v1.s.low64, v2.s.low64);
sum = vmlal_s16(sum, v1.s.high64, v2.s.high64);

printf("sum: %d\n", sum.n128_i32[0]);

1 个答案:

答案 0 :(得分:3)

这些不是直接等效的操作 - VMLA将两个向量相乘,然后将结果元素添加到第三个​​向量,与自包含的半元素半水平不同英特尔PMADDWD的疯狂。由于第3个向量是常规操作数,因此它必须存在于寄存器中,因此256位累加器没有空间。

如果您不希望使用VMLA执行8x16 * 8x16 + 8x16的风险溢出,则可以使用VMLAL执行4x16 * 4x16 + 4x32。显而易见的建议是将成对的指令组合成8x16向量处理成两个4x32累加器,然后在最后将它们加在一起,但我承认我对内在函数不太熟悉所以我不会这样做。知道他们有多难做到(与汇编相比,你可以利用这样的事实:" 64位向量"和#34; 128位向量"只是同一寄存器文件的可互换视图)。