我是SSE2指令的新手。我找到了一条可以添加两个数组元素的指令_mm_add_epi8
。但我想要一个可以添加数组所有元素的SSE指令。
我试图使用以下代码开发此概念:
#include <iostream>
#include <conio.h>
#include <emmintrin.h>
void sse(unsigned char* a,unsigned char* b);
void main()
{
/*unsigned char *arr;
arr=(unsigned char *)malloc(50);*/
unsigned char arr[]={'a','b','c','d','e','f','i','j','k','l','m','n','o','p','q','r','a','b','c','d','e','f','i','j','k','l','m','n','o','p','q','r'};
unsigned char *next_arr=arr+16;
for(int i=0;i<16;i++)
printf("%d,%c ",next_arr[i],next_arr[i]);
sse(arr,next_arr);
getch();
}
void sse(unsigned char* a,unsigned char* b)
{
__m128i* l = (__m128i*)a;
__m128i* r = (__m128i*)b;
__m128i result;
result= _mm_add_epi8(*l, *r);
unsigned char *p;
p=(unsigned char *)&result;
for(int i=0;i<16;i++)
printf("%d ",p[i]);
printf("\n");
l=(__m128i*)p;
r=(__m128i*)(p+8);
result=_mm_add_epi8(*l, *r);
p=(unsigned char *)&result;
printf("%d ",p[0]);
l=(__m128i*)p;
r=(__m128i*)(p+4);
result=_mm_add_epi8(*l, *r);
p=(unsigned char *)&result;
l=(__m128i*)p;
r=(__m128i*)(p+2);
result=_mm_add_epi8(*l, *r);
p=(unsigned char *)&result;
l=(__m128i*)p;
r=(__m128i*)(p+1);
result=_mm_add_epi8(*l, *r);
p=(unsigned char *)&result;
printf("result =%d ",p[0]);
}
那么有人可以告诉我如何使用SSE2指令添加数组的所有元素吗?
任何帮助将不胜感激。
答案 0 :(得分:19)
如果您只想对数组的所有元素求和,那么您需要加载数据,将其解压缩到更宽的元素大小,然后对解压缩的元素求和。请注意,您可以保持多个部分和,直到循环之后,然后只做这些部分和的最后一个和。例如:
uint32_t sum_array(const uint8_t a[], int n)
{
const __m128i vk0 = _mm_set1_epi8(0); // constant vector of all 0s for use with _mm_unpacklo_epi8/_mm_unpackhi_epi8
const __m128i vk1 = _mm_set1_epi16(1); // constant vector of all 1s for use with _mm_madd_epi16
__m128i vsum = _mm_set1_epi32(0); // initialise vector of four partial 32 bit sums
uint32_t sum;
int i;
for (i = 0; i < n; i += 16)
{
__m128i v = _mm_load_si128(&a[i]); // load vector of 8 bit values
__m128i vl = _mm_unpacklo_epi8(v, vk0); // unpack to two vectors of 16 bit values
__m128i vh = _mm_unpackhi_epi8(v, vk0);
vsum = _mm_add_epi32(vsum, _mm_madd_epi16(vl, vk1));
vsum = _mm_add_epi32(vsum, _mm_madd_epi16(vh, vk1));
// unpack and accumulate 16 bit values to
// 32 bit partial sum vector
}
// horizontal add of four 32 bit partial sums and return result
vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 8));
vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 4));
sum = _mm_cvtsi128_si32(vsum);
return sum;
}
请注意,上面的代码中有一个非显而易见的技巧 - 而不是将每个16位向量进一步解包为一对32位向量(需要4个解包指令),然后使用4个32位加法(另外4个指令) ,我们使用_mm_madd_epi16
(PMADDWD
)和1和_mm_add_epi32
的被乘数来有效地为我们提供免费解包,因此我们使用4条指令而不是8条获得相同的结果。
另请注意,输入数组a[]
需要16字节对齐,n
应为16的倍数。