通过线程和SIMD并行化矩阵乘法

我正在尝试加速多核架构上的矩阵乘法。 为此,我尝试同时使用线程和SIMD。 但我的结果并不好。 我通过顺序矩阵乘法测试加速:

void sequentialMatMul(void* params) { cout << "SequentialMatMul started."; int i, j, k; for (i = 0; i < N; i++) { for (k = 0; k < N; k++) { for (j = 0; j < N; j++) { X[i][j] += A[i][k] * B[k][j]; } } } cout << "\nSequentialMatMul finished."; } 

我尝试将线程和SIMD添加到矩阵乘法中,如下所示:

 void threadedSIMDMatMul(void* params) { bounds *args = (bounds*)params; int lowerBound = args->lowerBound; int upperBound = args->upperBound; int idx = args->idx; int i, j, k; for (i = lowerBound; i <upperBound; i++) { for (k = 0; k < N; k++) { for (j = 0; j < N; j+=4) { mmx1 = _mm_loadu_ps(&X[i][j]); mmx2 = _mm_load_ps1(&A[i][k]); mmx3 = _mm_loadu_ps(&B[k][j]); mmx4 = _mm_mul_ps(mmx2, mmx3); mmx0 = _mm_add_ps(mmx1, mmx4); _mm_storeu_ps(&X[i][j], mmx0); } } } _endthread(); } 

以下部分用于计算每个线程的下行和上行:

 bounds arg[CORES]; for (int part = 0; part < CORES; part++) { arg[part].idx = part; arg[part].lowerBound = (N / CORES)*part; arg[part].upperBound = (N / CORES)*(part + 1); } 

最后,线程SIMD版本被调用如下:

 HANDLE handle[CORES]; for (int part = 0; part < CORES; part++) { handle[part] = (HANDLE)_beginthread(threadedSIMDMatMul, 0, (void*)&arg[part]); } for (int part = 0; part < CORES; part++) { WaitForSingleObject(handle[part], INFINITE); } 

结果如下:测试1:

 // arrays are defined as follow float A[N][N]; float B[N][N]; float X[N][N]; N=2048 Core=1//just one thread 

连续时间:11129ms

螺纹SIMD matmul时间:14650ms

加速= 0.75x

测试2:

 //defined arrays as follow float **A = (float**)_aligned_malloc(N* sizeof(float), 16); float **B = (float**)_aligned_malloc(N* sizeof(float), 16); float **X = (float**)_aligned_malloc(N* sizeof(float), 16); for (int k = 0; k < N; k++) { A[k] = (float*)malloc(cols * sizeof(float)); B[k] = (float*)malloc(cols * sizeof(float)); X[k] = (float*)malloc(cols * sizeof(float)); } N=2048 Core=1//just one thread 

连续时间:15907ms

螺纹SIMD matmul时间:18578ms

加速= 0.85x

测试3:

 //defined arrays as follow float A[N][N]; float B[N][N]; float X[N][N]; N=2048 Core=2 

连续时间:10855ms

螺纹SIMD matmul时间:27967ms

加速= 0.38x

测试4:

 //defined arrays as follow float **A = (float**)_aligned_malloc(N* sizeof(float), 16); float **B = (float**)_aligned_malloc(N* sizeof(float), 16); float **X = (float**)_aligned_malloc(N* sizeof(float), 16); for (int k = 0; k < N; k++) { A[k] = (float*)malloc(cols * sizeof(float)); B[k] = (float*)malloc(cols * sizeof(float)); X[k] = (float*)malloc(cols * sizeof(float)); } N=2048 Core=2 

连续时间:16579ms

螺纹SIMD matmul时间:30160ms

加速= 0.51x

我的问题:为什么我没有加快速度?

以下是我在四核i7 IVB处理器上构建算法的时间。

 sequential: 3.42 s 4 threads: 0.97 s 4 threads + SSE: 0.86 s 

以下是2核P9600 @ 2.53 GHz的时间,类似于OP的E2200 @ 2.2 GHz

 sequential: time 6.52 s 2 threads: time 3.66 s 2 threads + SSE: 3.75 s 

我使用OpenMP因为它使这很容易。 OpenMP中的每个线程都有效地运行

 lowerBound = N*part/CORES; upperBound = N*(part + 1)/CORES; 

(请注意,这与您的定义略有不同。由于您首先除以CORES因此对于某些N值进行舍入,您的定义会给出错误的结果。)

至于SIMD版本。 它的速度并不快,可能是因为它受内存带宽限制 。 它可能不是真的更快,因为GCC已经对循环进行了测量。

最优化的解决方案要复杂得多。 您需要使用循环切片并对切片中的元素重新排序以获得最佳性能。 我今天没有时间这样做。

这是我使用的代码:

 //c99 -O3 -fopenmp -Wall foo.c #include  #include  #include  #include  void gemm(float * restrict a, float * restrict b, float * restrict c, int n) { for(int i=0; i 

在我看来,线程正在共享__m128 mmx*变量,您可能将它们定义为全局/静态。 您的Xarrays也必须得到错误的结果。 在threadedSIMDMatMul函数范围内定义__m128 mmx*变量,它运行得更快。

 void threadedSIMDMatMul(void* params) { __m128 mmx0, mmx1, mmx2, mmx3, mmx4; // rest of the code here }