openmpc++矩阵乘法并行运行较慢
OpenMP C++ Matrix Multiplication run slower in parallel
我正在学习使用OpenMP并行执行for循环的基础知识。
遗憾的是,我的并行程序运行速度比串行版本慢10倍。我做错了什么?我是否漏掉了一些障碍?
double **basicMultiply(double **A, double **B, int size) {
int i, j, k;
double **res = createMatrix(size);
omp_set_num_threads(4);
#pragma omp parallel for private(k)
for (i = 0; i < size; i++) {
for (j = 0; j < size; j++) {
for (k = 0; k < size; k++) {
res[i][j] += A[i][k] * B[k][j];
}
}
}
return res;
}
非常感谢!
您的问题是由于内循环变量j
上的竞争条件。它需要保密。
对于C89,我会这样做:
#pragma omp parallel
{
int i, j, k;
#pragma omp for
for(i=0; ...
对于c++或C99使用混合声明
#pragma omp parallel for
for(int i=0; ...
这样做,你不需要显式地声明任何共享或私有的东西。
对代码的进一步注释。当您执行B[k][j]
时,单线程代码对缓存不友好。这将读取一个cacheline,然后移动到下一个缓存行,以此类推,直到点积完成,此时其他cacheline已被驱逐。相反,你应该先进行转置,然后以BT[j][k]
的形式访问。此外,您分配了数组的数组,而不是一个连续的2D数组。我修复了你的代码使用转置和连续的二维数组。
这里是size=512的次数。
no transpose no openmp 0.94s
no transpose, openmp 0.23s
tranpose, no openmp 0.27s
transpose, openmp 0.08s
下面是代码(参见http://coliru.stacked-crooked.com/a/ee174916fa035f97)
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
void transpose(double *A, double *B, int n) {
int i,j;
for(i=0; i<n; i++) {
for(j=0; j<n; j++) {
B[j*n+i] = A[i*n+j];
}
}
}
void gemm(double *A, double *B, double *C, int n)
{
int i, j, k;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
double dot = 0;
for (k = 0; k < n; k++) {
dot += A[i*n+k]*B[k*n+j];
}
C[i*n+j ] = dot;
}
}
}
void gemm_omp(double *A, double *B, double *C, int n)
{
#pragma omp parallel
{
int i, j, k;
#pragma omp for
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
double dot = 0;
for (k = 0; k < n; k++) {
dot += A[i*n+k]*B[k*n+j];
}
C[i*n+j ] = dot;
}
}
}
}
void gemmT(double *A, double *B, double *C, int n)
{
int i, j, k;
double *B2;
B2 = (double*)malloc(sizeof(double)*n*n);
transpose(B,B2, n);
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
double dot = 0;
for (k = 0; k < n; k++) {
dot += A[i*n+k]*B2[j*n+k];
}
C[i*n+j ] = dot;
}
}
free(B2);
}
void gemmT_omp(double *A, double *B, double *C, int n)
{
double *B2;
B2 = (double*)malloc(sizeof(double)*n*n);
transpose(B,B2, n);
#pragma omp parallel
{
int i, j, k;
#pragma omp for
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
double dot = 0;
for (k = 0; k < n; k++) {
dot += A[i*n+k]*B2[j*n+k];
}
C[i*n+j ] = dot;
}
}
}
free(B2);
}
int main() {
int i, n;
double *A, *B, *C, dtime;
n=512;
A = (double*)malloc(sizeof(double)*n*n);
B = (double*)malloc(sizeof(double)*n*n);
C = (double*)malloc(sizeof(double)*n*n);
for(i=0; i<n*n; i++) { A[i] = rand()/RAND_MAX; B[i] = rand()/RAND_MAX;}
dtime = omp_get_wtime();
gemm(A,B,C, n);
dtime = omp_get_wtime() - dtime;
printf("%fn", dtime);
dtime = omp_get_wtime();
gemm_omp(A,B,C, n);
dtime = omp_get_wtime() - dtime;
printf("%fn", dtime);
dtime = omp_get_wtime();
gemmT(A,B,C, n);
dtime = omp_get_wtime() - dtime;
printf("%fn", dtime);
dtime = omp_get_wtime();
gemmT_omp(A,B,C, n);
dtime = omp_get_wtime() - dtime;
printf("%fn", dtime);
return 0;
}
另外。"Z玻色子",我在笔记本电脑上用英特尔i5(2个物理内核或4个逻辑内核)测试了你的C代码。不幸的是,计算速度不是很快。对于2000x2000随机双矩阵,我获得了以下结果(使用VS 2010与OpenMP 2.0):
编译于Win64: C = A*B,其中A,B是大小为(2000x2000)的矩阵:
max number of threads = 4
创建随机矩阵:= 0.303555秒
No转置No openmp = 100.539924 s
没有转置,openmp = 47.876084 s
转置,no openmp = 27.872169 s
转置,openmp = 15.821010 s
编译于Win32: C = A*B,其中A,B是大小为(2000x2000)的矩阵:
max number of threads = 4
创建随机矩阵:= 0.378804 s
No转置No openmp = 98.613992 s
没有转置,openmp = 48.233655 s
转置,no openmp = 29.590350 s
转置,openmp = 13.678097 s
请注意,对于"Hynek Blaha"代码,我的系统上的计算时间是739.208s (226.62s使用openMP)!
而在中Matlab x64:n = 2000;
A = rand(n); B = rand(n);
tic
C = A*B;
toc
计算时间0.591440秒。
但是使用openBLAS包,我达到了0.377814秒的速度(使用openMP 4.0的minGW)。Armadillo包提供了一种简单的方法(在我看来)将矩阵操作与openBLAS(或其他类似的包)连接起来。在本例中,代码是
#include <iostream>
#include <armadillo>
using namespace std;
using namespace arma;
int main(){
int n = 2000;
int N = 10; // number of repetitions
wall_clock timer;
arma_rng::set_seed_random();
mat A(n, n, fill::randu), B(n, n, fill::randu);
timer.tic();
// repeat simulation N times
for(int n=1;n<N;n++){
mat C = A*B;
}
cout << timer.toc()/double(N) << "s" << endl;
return 0;
}
如果size
很小,线程同步的开销将掩盖并行计算带来的任何性能增益。
- 为什么我不能让 3 个网络摄像头与 pthreads 并行运行?
- 我们如何并行运行算法的 n 个实例并以有效的方式计算结果函数的平均值?
- System Verilog DPI - 在 cpp 中运行并行线程,在 SV 中运行另一个并行线程
- 如果事件在仍在执行时再次设置,RegisterWaitForSingleObject 是否会并行运行回调?
- 如何并行运行与OpenGL并行的程序
- 在 2 个并行运行的脚本(python 和 C++)之间传递消息
- 在 C++ 中并行重复随机生成,避免多次运行重复
- 从Qt/C++-App并行运行两个Lua函数
- C++ - 谷歌测试 - 如何在不同的目录中并行运行所有测试?
- 如何使用 Turbo C++并行运行两个功能,一个用于键盘,一个用于鼠标?
- OpenMP 似乎不是并行运行的
- 使用 vector<thread> 和 .join() 未并行运行的多线程C++程序
- 最大线程数 - 如何确定C++线程是否并行运行?
- 并行和顺序运行代码时会产生不同的结果
- 如何在for循环(C )中运行并行线程
- 等待条件变量后未并行运行的线程
- 单元测试任务使用 QThreadPool 并行运行
- MFC多线程程序可以以并行方法运行
- 为依赖于先前值的矩阵运行并行代码
- OpenMP -在并行代码中运行并行代码