2015-02-24 54 views
1

我写了一个矩阵向量乘法的代码。矩阵根据线程的数量划分成若干行,每个块乘以向量,向量存储在线程专用的数组中。但是我的加速非常糟糕。对于大小为16×16的矩阵,它低于1.OpenMp代码的性能

这是否可以归因于以下事实:我将外部矩阵和向量声明为共享变量,并且可能在每个线程试图读取时导致竞争条件/错误共享矩阵和向量的值?

我有点混淆错误分享和竞争条件。

#include <stdio.h> 
#include <omp.h> 
#include <stdlib.h> 
#define SIZE 128    // The size should be divisible by thenumber of threads 

int main(int argc, char *argv[]) { 

int thread_count = strtol(argv[1],NULL,10); 
// Declare the variables 
int i,j; 
long A[SIZE][SIZE], b[SIZE],V[SIZE]={0}; 
//long Vect[SIZE]={0}; 
double start, end; 
// Generate a matrix of size mxm 
for (i=0; i<SIZE; i++) 
{ for (j=0; j<SIZE; j++) 
    A[i][j] = i+j; 
} 

printf("The Matrix is:\n"); 
// Print the Matrix 
for (i=0; i<SIZE; i++) 
{ for (j=0; j<SIZE; j++) 
     { 
     printf("%12ld", A[i][j]); 
     } 
printf("\n"); 

} 

// Generate a vector of size m 
for (i=0; i<SIZE; i++) 
    b[i] = i; 

printf("The vector is: \n"); 
// Print a vector 
for (i=0; i<SIZE; i++) 
    printf("%12ld\n", b[i]); 


start = omp_get_wtime(); 
//omp_set_num_threads(NUM_THREADS); 

#pragma omp parallel num_threads(thread_count) 
{ 
int i,j,k, id, nthrds; 
long Vect[SIZE]={0}; 
id = omp_get_thread_num(); 
nthrds = omp_get_num_threads(); 
for (i=id*SIZE/nthrds; i<(id*SIZE/nthrds + SIZE/nthrds); i++) 
{ Vect[i] = 0; 
    { 
     for (j=0; j<SIZE; j++) 
     Vect[i] += A[i][j]*b[j]; 
    } 

} 

#pragma omp critical 
{ 
for (k=0; k<SIZE; k++) 
V[k] += Vect[k]; 
} 
} 


end = omp_get_wtime(); 
printf("The vector obtained after multiplication is:\n"); 
for (i=0; i<SIZE; i++) 
printf("%12ld\n", V[i]); 
printf("The time taken for calculation is: %lf\n", end - start); 


return 0; 

} 
+0

这很可能是一个工作量小(每个线程只做256/num_thread乘加),设定的开销多线程并行化的速度比并行化的速度更快。是的,在线程之间共享写入状态很可能使并行化开销更高。 – aruisdante 2015-02-24 18:04:32

+0

欲了解更多关于虚假分享:http://stackoverflow.com/questions/9027653/openmp-false-sharing?rq=1。对于一般的OpenMP性能的一些有趣的讨论:http://stackoverflow.com/questions/10939158/openmp-performance?rq=1 – aruisdante 2015-02-24 18:10:44

+0

@aruisdante没有共享写入,有共享读取 – 2015-02-24 21:39:31

回答

0

让我提出一些改进代码的建议。

  1. 这几乎不是一个好主意或必须手工并行化for循环。其中一个原因是它容易出错。

    for (i=id*SIZE/nthrds; i<(id*SIZE/nthrds + SIZE/nthrds); i++) 
    

    应改为

    for (i=id*SIZE/nthrds; i<((id+1)*SIZE/nthrds; i++) 
    

    否则为nthrds某些值的结果是错误的。

    但是不要自己定义块,让OpenMP为您做这件事。

    #pragma omp parallel for private(j) 
    for(i=0; i<SIZE; i++) { 
        long sum = 0; 
        for(j=0; j<SIZE; j++) { 
         sum += A[i][j]*b[j]; 
        } 
        V[i] += sum; 
    } 
    
  2. 你说得对写入V时担心假共享。但是,不需要为每个线程定义一个数组Vect。上面的代码通过在内部循环内定义sum来解决您关心的错误共享问题。此代码仍然存在虚假分享,但并非针对所有ij迭代(SIZE*SIZE),而是仅针对所有i迭代(SIZE)。

  3. 12812的太小而无法克服OpenMP开销。当我使用8192的尺寸时,我发现在串行代码上有了显着的改进。但是,对于较大的大小,您的代码还存在另一个问题,因为您的数组使用了受堆栈大小限制的自动变量。我建议你使用不受堆栈大小限制的静态变量。

  4. 最后,使用num_threads来比较串行代码是不公平的。原因是编译器内置了OpenMP支持,即使是num_threads(1)。这偏离了结果。相反,您应该比较是否启用OpenMP。不幸的是,GCC不允许你在不启用OpenMP的情况下使用omp_get_wtime()(尽管MSVC和ICC)。因此,如果您在使用GCC比较串行代码时注释掉编译指示。使用ICC,您只能启用存根功能。使用MSVC不会启用OpenMP(omp_get_wtime()仍然有效)。

下面是针对每个点的代码:

#include <stdio.h> 
#include <omp.h> 
#define SIZE 8192 

int main(void) { 
    int i,j; 
    double dtime; 
    static long A[SIZE][SIZE], b[SIZE],V[SIZE]; 
    for (i=0; i<SIZE; i++) { 
     for (j=0; j<SIZE; j++) { 
      A[i][j] = i+j; 
     } 
    } 
    for (i=0; i<SIZE; i++) b[i] = i; 

    dtime = -omp_get_wtime(); 
    #pragma omp parallel for private(j) //comment out for one thread 
    for(i=0; i<SIZE; i++) { 
     long sum = 0; 
     for(j=0; j<SIZE; j++) { 
      sum += A[i][j]*b[j]; 
     } 
     V[i] += sum; 
    }  
    dtime += omp_get_wtime(); 
    printf("The time taken for calculation is: %lf\n", dtime); 

    return 0; 
}