SSE指令集实现reduce归约操作

通俗的说这里的归约就是求和,比如:
for(i = 0; i < n; i++){
sum += a[i];
}
这里主要对比较正常写代码的归约和循环展开以及SSE指令向量归约操作的比较。
话不多说,上代码:

#include <stdio.h>
#include <x86intrin.h>
#include <time.h>
#include <stdlib.h>
//正常处理的数组归约
int normal_add(int *a, int n){
	int sum = 0;
	int i;
	for(i = 0; i < n; i++){
		sum += a[i];
	}
	return sum;
}
//正常数组归约的四层循环展开
int normal_add_loop(int *a, int n){
	int sum = 0;
	int i;
	int block = n / 4;
	int reserve = n % 4;
	for(i = 0; i < block; i++){
		sum += *a;
		sum += *(a+1);
		sum += *(a+2);
		sum += *(a+3);
		a += 4;
	}
	for(i = 0; i < reserve; i++){
		sum += a[i];
	}
	return sum;
}
//sse指令向量归约(一个寄存器,一次取4个数)
int sse_add(int *a, int n){
	int sum = 0;
    	__m128i sse_sum = _mm_setzero_si128();
    	__m128i sse_load;
	int i;
    	int block = n / 4;     // SSE寄存器能一次处理4个32位的整数
    	int reserve = n % 4;  // 剩余的不足16字节
		__m128i *p = (__m128i *)a;
    	for(i = 0; i < block; i++) {
        	sse_load = _mm_load_si128(p + i);
        	sse_sum  = _mm_add_epi32(sse_sum, sse_load); // 带符号32位紧缩加法       
   	}

		// 剩余的不足16字节
    	for(i = 0; i < reserve; i++) {
        	sum += a[i + block * 4];
    	}

		// 将累加值合并
    	sse_sum = _mm_hadd_epi32(sse_sum, sse_sum);  // 带符号32位水平加法
    	sse_sum = _mm_hadd_epi32(sse_sum, sse_sum);

    	sum += _mm_cvtsi128_si32(sse_sum);  // 返回低32位
		// 或者 sum += sse_sum[0];
    	return sum;
}
//sse指令向量归约(四个寄存器,一次循环去16个值)
int sse_add_loop4(int *a, int n){
	int sum = 0;
	int i;
	//四个sse寄存器一次处理16 个数
	int block = n / 16;
	//不足16个数的部分
	int reserve = n % 16;
	__m128i sse_sum0 = _mm_setzero_si128();
	__m128i sse_sum1 = _mm_setzero_si128();
	__m128i sse_sum2 = _mm_setzero_si128();
	__m128i sse_sum3 = _mm_setzero_si128();
	__m128i sse_load0;
	__m128i sse_load1;
	__m128i sse_load2;
	__m128i sse_load3;
	__m128i *p = (__m128i *)a;
	for(i = 0; i < block; i++){
		sse_load0 = _mm_load_si128(p);
		sse_load1 = _mm_load_si128(p + 1);
		sse_load2 = _mm_load_si128(p + 2);
		sse_load3 = _mm_load_si128(p + 3);

		sse_sum0 = _mm_add_epi32(sse_sum0, sse_load0);
		sse_sum1 = _mm_add_epi32(sse_sum1, sse_load1);
		sse_sum2 = _mm_add_epi32(sse_sum2, sse_load2);
		sse_sum3 = _mm_add_epi32(sse_sum3, sse_load3);
		p = p+4;
	}
	//不足16个数的部分
	int dev = block * 16;
	for(i = 0; i < reserve; i++){
		sum += a[i + dev];
	}

	sse_sum0 = _mm_add_epi32(sse_sum0, sse_sum1);
    sse_sum2 = _mm_add_epi32(sse_sum2, sse_sum3);
    sse_sum0 = _mm_add_epi32(sse_sum0, sse_sum2);

    sse_sum0 = _mm_hadd_epi32(sse_sum0, sse_sum0);
    sse_sum0 = _mm_hadd_epi32(sse_sum0, sse_sum0);
    sum += _mm_cvtsi128_si32(sse_sum0); // 取低32位
	return sum;

}
//数组赋值
void value (int *a, int n){
	int i, t = 1;
	for(i = 0; i < n; i++){
		a[i] = t;
		if(i > 0 && i % 10000 == 0){
			t++;
		}	
	}
}
//打印数组
void print(int *a, int n){
	int i;
	for(i = 0; i < n; i++){
		printf("%d	", a[i]);
	}
}

int main(){
	clock_t s, e;
	int n = 100000000;
	int *a = malloc(sizeof(int) * n);
	value(a, n);

	//normal_add_sum
	s = clock();
	int normal_add_sum = normal_add(a , n);
	e = clock();
	float time = (float)(e - s) / CLOCKS_PER_SEC;
	printf("normal_add_sum = %d, time = %f \n",normal_add_sum, time);
	
	//normal_add_loop_sum
	s = clock();
	int normal_add_loop_sum = normal_add_loop(a, n);
	e = clock();
	time = (float)(e - s) / CLOCKS_PER_SEC;
	printf("normal_add-loop_sum = %d, time = %f \n", normal_add_loop_sum, time);
	
	//sse_add_sum
	s = clock();
	int sse_add_sum = sse_add(a, n);
	e = clock();
	time = (float)(e - s) / CLOCKS_PER_SEC;
	printf("sse_add_sum = %d, time = %f \n", sse_add_sum, time);

	//sse_add_loop_sum
	s = clock();
	int sse_add_loop4_sum = sse_add_loop4(a, n);
	e = clock();
	time = (float)(e - s) / CLOCKS_PER_SEC;
	printf("sse_add_loop4_sum = %d, time = %f \n", sse_add_loop4_sum, time);

	free(a);

}

编译:
clang -mssse3 reduce.c -o reduce
运行:
./reduce
结果:
结果

猜你喜欢

转载自blog.csdn.net/weixin_42826139/article/details/86357426
今日推荐