【发布时间】:2018-08-03 10:09:26
【问题描述】:
所以我试图将一个带有 short int a[101] 的常数与 intel 内在函数相乘。我已经用加法完成了,但我似乎无法弄清楚为什么它不适用于乘法。同样,在我们使用 32 位整数之前,现在我们使用 16 位短整数,因此据我所知,我们可以在内部函数中有两倍多的值来填充 128 位?
我正在尝试做的简单示例:
int main(int argc, char **argv){
short int a[101];
int len = sizeof(a)/sizeof(short);
/*Populating array a with values 1 to 101*/
mult(len, a);
return 0;
}
int mult(int len, short int *a){
int result = 0;
for(int i=0; i<len; i++){
result += a[i]*20;
}
return result;
}
我的代码试图在内部函数中做同样的事情
/*Same main as before with a short int a[101] containing values 1 to 101*/
int SIMD(int len, short int *a){
int res;
int val[4];
/*Setting constant value to mulitply with*/
__m128i sum = _mm_set1_epi16(20);
__m128i s = _mm_setzero_si128( );
for(int i=0; i<len/4*4; i += 4){
__m128i vec = _mm_loadu_si128((__m128i *)(a+i));
s += _mm_mul_epu32(vec,sum);
}
_mm_storeu_si128((__m128i*) val, s);
res += val[0] + val[1] + val[2] + val[3];
/*Haldeling tail*/
for(int i=len/4*4; i<len; i++){
res += a[i];
}
return res;
}
所以我确实得到了一个数字作为结果,但该数字与天真的方法不匹配,我尝试了其他内在函数并更改数字以查看它是否有任何显着差异,但没有任何东西接近我期望的输出。计算时间也和现在的naive差不多。
【问题讨论】:
-
"s = _mm_mul_epu32(vec,sum);"看起来很奇怪。你的意思是 s += 吗?
-
是的应该是+=。但是结果还是奇数(-1889431611)
-
res永远不会被初始化。一个 128 位 SSE 块中有 8 个 16 位整数,而不是 4 个。所以你应该在有 4 个的地方使用 8。val应该是short val[8],并将表达式val[0] + val[1] + val[2] + val[3]扩展为 @987654328 @。_mm_mul_epu32乘以 32 位元素;乘以 16 位元素应该是_mm_mullo_epi16。语句s = _mm_mul_epu32(vec,sum);仅将vec与sum相乘,但您希望将它们相乘,然后将乘积添加到s。 -
@BjornA.:
s += ...将使用paddq(64 位元素大小),因为 GNU C 中的__m128i被定义为typedef long long __m128i __attribute__((vector_size(16), may_alias))。但是,嘿,OP 已经在 16 位数据上使用 32 位元素乘法,当没有从一个元素到下一个元素的进位(溢出时)时,这个错误甚至不会导致损坏。 -
要编写这个便携,使用
_mm_add_epi16而不是GNU C / C+++=。或者更好的是,使用_mm_madd_epi16(pmaddwd) 进行乘法以将水平元素对组合成 32 位总计而不会溢出,然后使用_mm_add_epi32
标签: c optimization intel intrinsics