从多个线程读取数组时要注意啥?
Posted
技术标签:
【中文标题】从多个线程读取数组时要注意啥?【英文标题】:What to heed, when reading an array from multiple threads?从多个线程读取数组时要注意什么? 【发布时间】:2014-09-25 07:27:24 【问题描述】:我想稍微了解一下 OpenMP,因为我想并行化一个巨大的循环。经过一番阅读(SO、Common OMP mistakes、tutorial 等),我已将下面给出的基本工作的 c/mex 代码作为第一步(对于第一个测试用例会产生不同的结果)。
第一个测试确实总结了结果值 - 函数serial, parallel
-,
第二个从输入数组中获取值并将处理后的值写入输出数组 - 函数 serial_a, parallel_a
。
我的问题是:
-
为什么第一次测试的结果不同,我。 e.
serial
和 parallel
的结果
令人惊讶的是,第二次测试成功了。我关心的是,如何处理可能被多个线程读取的内存(数组位置)?在示例中,这应该由 a[i])/cos(a[n-i]
模拟。
是否有一些简单的规则可以确定哪些变量要声明为 private、shared 和 reduction?
在这两种情况下int i
都在pragma
之外,但是第二个测试似乎产生了正确的结果。那么这样可以吗,还是将i
移到pragma omp parallel
区域as being said here?
关于发现错误的任何其他提示?
代码
#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);
请注意,此代码与您的序列号之间的唯一区别是编译指示语句。 OpenMP 旨在让您无需更改代码,除了编译指示语句。
当涉及到数组时,只要并行 for 循环的每次迭代都作用于不同的数组元素,那么您就不必担心共享和私有。因此,您可以将 private_a
函数简单地编写为
#pragma omp parallel for
for(i = 0; i<n; i++)
y2[i] = sin(a[i]) / cos(a[n-i]+1.0);
除 pragma 语句外,它再次与您的 serial_a
函数相同。
但要小心假设迭代器是私有的。考虑以下双循环
for(i=0; i<n; i++)
for(j=0; j<m; j++)
//
如果您使用#pragma parallel for
,则i
迭代器将被设为私有,但j
迭代器将被共享。这是因为parallel for
仅适用于i
上的外部循环,并且由于j
默认情况下是共享的,因此它不会设为私有。在这种情况下,您需要像 #pragma parallel for private(j)
一样显式声明 j
私有。
【讨论】:
只要并行 for 循环的每次迭代作用于不同的数组元素,那么您就不必担心共享和私有了。但是如果数组的相同元素必须由不同的线程读取呢? OMP 是否会处理这个问题,例如。 G。一个线程只是等待一个元素被另一个元素读取?或者阅读权限根本没有问题?那么如果一个元素可能必须由不同的线程更改呢?这可能吗? 如果数组的相同元素必须由不同的线程读取,您无需担心这一点。每个线程都会将数据拉入其本地缓存。但是,您必须担心是否有多个线程会写入同一个数组元素。这可能会导致竞争条件。您也可能在写入不同但在本地关闭的元素时遇到问题。这称为虚假分享。以上是关于从多个线程读取数组时要注意啥?的主要内容,如果未能解决你的问题,请参考以下文章