2015-02-24 97 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; 
}