所以我试图将一个常量与短 int a[101] 与英特尔内在函数相乘。我已经用加法完成了它,但我似乎无法弄清楚为什么它不适用于乘法。另外,在我们使用 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 几乎相同。
最佳答案
一个__m128i
中有8个short
。所以:
for(int i=0; i<len/4*4; i += 4)
应该是
for(int i=0; i<len/8*8; i += 8)`
和:
res += val[0] + val[1] + val[2] + val[3];
应该是:
res += val[0] + val[1] + val[2] + val[3] + val[4] + val[5] + val[6] + val[7];
和:
for(int i=len/4*4; i<len; i++)
应该是:
for(int i=len/8*8; i<len; i++)
在:
s += _mm_mul_epu32(vec,sum);
_mm_mul_epu32
在 32 位元素上运行。应该是:
s += _mm_mullo_epi16(vec, sum);
对象res
未初始化;应该是:
int res = 0;
这是工作代码:
#include <stdio.h>
#include <stdlib.h>
#include <immintrin.h>
// Number of elements in an array.
#define NumberOf(x) (sizeof (x) / sizeof *(x))
// Compute the result with scalar arithmetic.
static int mult(int len, short int *a)
{
int result = 0;
for (size_t i=0; i<len; i++)
{
result += a[i]*20;
}
return result;
}
// Compute the result with SIMD arithmetic.
static int SIMD(int len, short int *a)
{
// Initialize the multiplier and the sum.
__m128i multiplier = _mm_set1_epi16(20);
__m128i s = _mm_setzero_si128( );
// Process blocks of 8 short.
for (int i=0; i<len/8*8; i += 8)
{
__m128i vec = _mm_loadu_si128((__m128i *)(a+i));
// Multtiply by multiplier and add to sum.
s = _mm_add_epi16(s, _mm_mullo_epi16(vec, multiplier));
}
// Store the sum so far so its individual elements can be manipulated.
short val[8];
_mm_storeu_si128((__m128i*) val, s);
// Add the individual elements.
int res = 0;
for (size_t i = 0; i < 8; ++i)
res += val[i];
// Add the elements in the tail.
for (size_t i = len/8*8; i < len; ++i)
{
res += a[i];
}
return res;
}
int main(int argc, char **argv)
{
short int a[96];
int len = NumberOf(a);
// Initiailize a.
for (size_t i = 0; i < len; ++i)
a[i] = i+1;
printf("sum by scalar arithmetic is %d.\n", mult(len, a));
printf("sum by SIMD arithmetic is %d.\n", SIMD(len, a));
return 0;
}
关于c - Intel Intrinsics 代码优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48946384/