2014-09-25 52 views
0

我想了解一下OpenMP,因爲我想要一個巨大的並行循環。經過一些閱讀(SO,Common OMP mistakes,tutorial等),我已經採取了第一步基本工作的c/mex代碼下面給出(這產生了第一個測試用例不同的結果)。從多線程讀取數組時需要注意什麼?

  • 第一測試做總結結果值 - 功能serial, parallel - ,
  • 第二從輸入陣列取值並寫入處理的值到一個輸出陣列 - 功能serial_a, parallel_a

我的問題是:

  1. 爲什麼不同初試的結果,我。即serialparallel
  2. 的結果令人驚訝的是第二個測試成功。我關心的是,如何處理可能被多線程讀取的內存(陣列位置)?在這個例子中,這應該被a[i])/cos(a[n-i]模擬。
  3. 是否有一些簡單的規則如何確定申報爲私人共享減少哪些變量?
  4. 在這兩種情況下,int i都在pragma之外,但第二個測試看起來會產生正確的結果。那麼是好的還是已經將i移到pragma omp parallel區域,as being said here
  5. 任何其他暗示錯誤的提示?

代碼

#include "mex.h" 
#include <math.h> 
#include <omp.h> 
#include <time.h> 

double serial(int x) 
{ 
    double sum=0; 
    int i; 

    for(i = 0; i<x; i++){ 
     sum += sin(x*i)/cos(x*i+1.0); 
    } 
    return sum; 
} 

double parallel(int x) 
{ 
    double sum=0; 
    int i; 

    #pragma omp parallel num_threads(6) shared(sum) //default(none) 
    { 
     //printf(" I'm thread no. %d\n", omp_get_thread_num()); 

     #pragma omp for private(i, x) reduction(+: sum) 
     for(i = 0; i<x; i++){ 
      sum += sin(x*i)/cos(x*i+1.0); 
     } 
    } 
    return sum; 
} 

void serial_a(double* a, int n, double* y2) 
{ 
    int i; 

    for(i = 0; i<n; i++){ 
     y2[i] = sin(a[i])/cos(a[n-i]+1.0); 
    } 
} 

void parallel_a(double* a, int n, double* y2) 
{ 
    int i; 

    #pragma omp parallel num_threads(6) 
    {  
     #pragma omp for private(i) 
     for(i = 0; i<n; i++){ 
      y2[i] = sin(a[i])/cos(a[n-i]+1.0); 
     } 
    } 
} 

void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) 
{ 
    double sum, *y1, *y2, *a, s, p; 
    int x, n, *d; 

    /* Check for proper number of arguments. */ 
    if(nrhs!=2) { 
     mexErrMsgTxt("Two inputs required."); 
    } else if(nlhs>2) { 
     mexErrMsgTxt("Too many output arguments."); 
    } 
    /* Get pointer to first input */ 
    x = (int)mxGetScalar(prhs[0]); 

    /* Get pointer to second input */ 
    a = mxGetPr(prhs[1]); 
    d = (int*)mxGetDimensions(prhs[1]); 
    n = (int)d[1]; // row vector 

    /* Create space for output */ 
    plhs[0] = mxCreateDoubleMatrix(2,1, mxREAL); 
    plhs[1] = mxCreateDoubleMatrix(n,2, mxREAL); 

    /* Get pointer to output array */ 
    y1 = mxGetPr(plhs[0]); 
    y2 = mxGetPr(plhs[1]); 

    { /* Do the calculation */ 
     clock_t tic = clock(); 
     y1[0] = serial(x); 
     s = (double) clock()-tic; 
     printf("serial....: %.0f ms\n", s); 
     mexEvalString("drawnow"); 

     tic = clock(); 
     y1[1] = parallel(x); 
     p = (double) clock()-tic; 
     printf("parallel..: %.0f ms\n", p); 
     printf("ratio.....: %.2f \n", p/s); 
     mexEvalString("drawnow"); 

     tic = clock(); 
     serial_a(a, n, y2); 
     s = (double) clock()-tic; 
     printf("serial_a..: %.0f ms\n", s); 
     mexEvalString("drawnow"); 

     tic = clock(); 
     parallel_a(a, n, &y2[n]); 
     p = (double) clock()-tic; 
     printf("parallel_a: %.0f ms\n", p); 
     printf("ratio.....: %.2f \n", p/s); 
    } 
} 

輸出

>> mex omp1.c 
>> [a, b] = omp1(1e8, 1:1e8); 
serial....: 13399 ms 
parallel..: 2810 ms 
ratio.....: 0.21 
serial_a..: 12840 ms 
parallel_a: 2740 ms 
ratio.....: 0.21 
>> a(1) == a(2) 

ans = 

    0 

>> all(b(:,1) == b(:,2)) 

ans = 

    1 

系統

MATLAB Version: 8.0.0.783 (R2012b) 
Operating System: Microsoft Windows 7 Version 6.1 (Build 7601: Service Pack 1) 
Microsoft Visual Studio 2005 Version 8.0.50727.867 

回答

1

在你的函數parallel你有幾個錯誤。當您使用parallel時應該聲明縮小。當您使用parallel時,還應聲明私有和共享變量。但是當你減少時,你不應該聲明正在被減少爲共享的變量。減少將照顧這一點。

要知道什麼要聲明私人或共享,你必須問自己哪些變量正在寫入。如果一個變量沒有被正常寫入,你希望它被共享。在你的情況下,變量x不會改變,所以你應該聲明它是共享的。變量i,但是,不改變,因此正常情況下你應該私有聲明它使解決您的功能,你可以做

#pragma omp parallel reduction(+:sum) private(i) shared(x) 
{ 
    #pragma omp for 
    for(i = 0; i<x; i++){ 
     sum += sin(x*i)/cos(x*i+1.0); 
    } 
} 

然而,OpenMP的自動使並行的迭代器區私人和並行區域之外聲明的變量默認共享,以便爲您的並行功能,你可以簡單地做

#pragma omp parallel for reduction(+:sum) 
for(i = 0; i<x; i++){ 
    sum += sin(x*i)/cos(x*i+1.0); 
} 

注意,這和您的序列代碼之間的唯一區別是編譯statment。 OpenMP旨在讓您不必更改除編譯指示語句之外的代碼。

說到數組,只要parallel for循環的每次迭代都作用於不同的數組元素,則不必擔心共享和私有。所以,你可以寫你的private_a功能僅僅作爲

#pragma omp parallel for 
for(i = 0; i<n; i++){ 
    y2[i] = sin(a[i])/cos(a[n-i]+1.0); 
} 

,並再次它是一樣的,除了該編譯語句的serial_a功能。

但是要小心假設迭代器是私有的。請看下面的雙迴路

for(i=0; i<n; i++) { 
    for(j=0; j<m; j++) { 
     // 
    } 
} 

如果使用#pragma parallel for與該i迭代器將被設爲不公開,但j迭代器將被共享。這是因爲parallel for僅適用於i以上的外部循環,並且由於j在默認情況下共享,所以它不是私有的。在這種情況下,您需要明確聲明j私有,如#pragma parallel for private(j)

+0

*只要parallel for循環的每次迭代都作用於不同的數組元素,則不必擔心共享和私有*好。但是如果一個數組的相同元素必須被不同的線程讀取呢? OMP是否照顧這一點,例如, G。一個線程只是等待一個元素被另一個元素讀取的情況?或者讀取訪問不是問題呢?那麼情況如何,一個元素可能需要通過不同的線程來改變?這可能嗎? – embert 2014-09-26 03:49:56

+0

如果數組中的相同元素必須由不同的線程讀取,則不需要擔心這一點。每個線程都會將數據提取到本地緩存中。但是,如果多於一個線程將寫入相同的數組元素,則不必擔心。這可能會導致競爭條件。您也可以在寫入不同的元素時遇到問題,但在本地關閉。這被稱爲虛假分享。 – 2014-09-26 07:43:09

相關問題