Hyn*_*aha 13 c++ openmp matrix-multiplication
我正在学习使用OpenMP执行for循环的paralel的基础知识.
可悲的是,我的paralel程序运行速度比串行版慢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;
}
Run Code Online (Sandbox Code Playgroud)
非常感谢你!
Z b*_*son 26
你的问题是由于内循环变量的竞争条件造成的j.它需要私有化.
对于C89,我会做这样的事情:
#pragma omp parallel
{
int i, j, k;
#pragma omp for
for(i=0; ...
Run Code Online (Sandbox Code Playgroud)
对于C++或C99,请使用混合声明
#pragma omp parallel for
for(int i=0; ...
Run Code Online (Sandbox Code Playgroud)
这样做,您不必显式声明任何共享或私有.
对您的代码进一步评论.当你这样做时,你的单线程代码不是缓存友好的B[k][j].这将读取高速缓存行,然后移动到下一个高速缓存行,依此类推,直到完成点积,此时其他高速缓存行已被驱逐.相反,你应该首先采用转置并访问为BT[j][k].此外,您已分配数组数组,而不是一个连续的2D数组.我修复了你的代码以使用转置和连续的2D数组.
以下是尺寸= 512的时间.
no transpose no openmp 0.94s
no transpose, openmp 0.23s
tranpose, no openmp 0.27s
transpose, openmp 0.08s
Run Code Online (Sandbox Code Playgroud)
下面是代码(另见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("%f\n", dtime);
dtime = omp_get_wtime();
gemm_omp(A,B,C, n);
dtime = omp_get_wtime() - dtime;
printf("%f\n", dtime);
dtime = omp_get_wtime();
gemmT(A,B,C, n);
dtime = omp_get_wtime() - dtime;
printf("%f\n", dtime);
dtime = omp_get_wtime();
gemmT_omp(A,B,C, n);
dtime = omp_get_wtime() - dtime;
printf("%f\n", dtime);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
此外。“Z boson”,我已经在带有 intel i5(2 个物理内核或 4 个逻辑内核)的笔记本电脑上测试了您的 C 代码。不幸的是,计算速度不是很快。对于2000x2000随机双矩阵,我获得了以下结果(使用 VS 2010 和 OpenMP 2.0):
为 Win64 编译:C = A*B,其中 A、B 是大小为 (2000x2000) 的矩阵:
最大线程数 = 4
创建随机矩阵:= 0.303555 s
无转置无 openmp = 100.539924 s
无转置,openmp = 47.876084 s
转置,无 openmp = 27.872169 s
转置,openmp = 15.8210
为 Win32 编译:C = A*B,其中 A、B 是大小为 (2000x2000) 的矩阵:
最大线程数 = 4
创建随机矩阵:= 0.378804 s
无转置无 openmp = 98.613992 s
无转置,openmp = 48.233655 s
转置,无 openmp = 29.590350 s
转置,openmp = 13.67809
请注意,对于“Hynek Blaha”代码,我系统上的计算时间为739.208s(openMP为 226.62s)!
而在Matlab x64 中:
n = 2000;
A = rand(n); B = rand(n);
tic
C = A*B;
toc
Run Code Online (Sandbox Code Playgroud)
计算时间为0.591440 秒。
但是使用openBLAS包我达到了0.377814 秒的速度(使用 minGW 和 openMP 4.0)。将犰狳包提供了与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;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
17602 次 |
| 最近记录: |