2014-09-25 50 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

相关问题