1. 程式人生 > >SSE指令集實現reduce歸約操作

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
結果:
結果