[英]OpenMP matrix multiplication takes more time than expected
我正在编写一个 OpenMP 程序来将两个矩阵相乘。 这个想法是每个线程计算每个单元格结果的一部分。 然后,在那之后,我为每个单元格添加这些结果以获得乘法结果。
问题是,当我使用大型矩阵(512x512 或 1024x1024)时,该程序需要很长时间。 实际上,当我使用 5 个线程使用大小为 1024x1024 的矩阵时,需要 43 秒,而使用 1 个线程则需要 14 秒。
我在想这可能是导致巨大延迟的关键部分。
这是代码:
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
int ** make_array(int n,int m,int f)
{
int i,j;
int *linear, **arr;
linear = malloc(sizeof(int)*m*n);
arr = malloc(sizeof(int *)*n);
for(i = 0;i<n;++i) arr[i] = &linear[i*m];
if(f == 0)
{
for(i=0;i<n;++i)
for(j=0;j<m;++j) arr[i][j] = 0;
return arr;
}
for(i=0;i<n;++i)
for(j=0;j<m;++j) arr[i][j] = 1+i;
return arr;
}
void printMat(int **mat, int n)
{
int i,j;
for(i = 0; i < n; ++i)
{
for(j = 0; j < n;++j)
{
printf("%d ",mat[i][j]);
}
printf("\n");
}
}
int main (int argc, char *argv[])
{
int n; /// matrix dimension
scanf("%d", &n);
double TIME = 0;
int **a,**b,**c;
a = make_array(n,n,1);
b = make_array(n,n,1);
c = make_array(n,n,0);
int i,j,k;
#pragma omp parallel private(i,j,k) shared(a,b,c,TIME)
{
double start = omp_get_wtime();
int **local;
local = make_array(n,n,0);
for(i = 0; i < n; ++i)
{
for(j = 0; j <n; ++j)
{
local[i][j] = 0;
#pragma omp for schedule(static)
for(k = 0; k < n; ++k)
{
local[i][j]+= a[i][k] * b[k][j];
}
}
}
for(i = 0; i <n;++i)
{
for(j = 0; j < n; ++j)
{
#pragma omp critical
c[i][j] += local[i][j];
}
}
double end = omp_get_wtime();
if(TIME < end - start)
{
#pragma omp critical
TIME = end - start;
}
}
printf("%f \n", TIME);
}
任何帮助将非常感激。
这段代码有很多问题。
并行化方法效率很低:
对于每个可能的i
和j
,您将一个非常小的工作共享给多个线程。 此外,在并行 for 循环的末尾有一个隐式障碍。 因此,线程之间的通信可能比实际计算花费更多的时间。
临界区通常很慢(通常使用锁来实现)。 在这里,您可以用原子操作替换它。
使用 k 个线程,代码需要 k 次更多 memory 并且很可能是 memory 绑定的(因为缓存和要填充的更多数据,更不用说现在昂贵的额外页面错误)。
因此,您需要重新设计并行化方法。 例如,您可以在基于i
的循环上移动#pragma omp for schedule(static)
。 或者,您可以将矩阵分成块并在线程之间共享工作。
请使用BLAS库进行矩阵乘法。 它们比这段代码优化得多。
以下是一些其他问题的列表:
malloc
但没有free
的)。TIME
的条件存在竞争条件。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.