Dependencies OpenMP中的并行累积(前缀)和:线程之间的值通信
假设我有一个函数Dependencies OpenMP中的并行累积(前缀)和:线程之间的值通信,dependencies,sum,openmp,Dependencies,Sum,Openmp,假设我有一个函数f(I),它依赖于索引I(以及其他无法预计算的值)。 我想填充一个数组a,以便a[n]=sum(f(I))从I=0到n-1 编辑:在赫里斯托·伊利耶夫发表评论后,我意识到我现在所做的是一件好事 这可以用以下代码编写: float sum = 0; for(int i=0; i<N; i++) { sum += f(i); a[i] = sum; } 但是,不使用OpenMP的非并行循环也可以做到这一点。然而,我所提出的解决方案是复杂的,也许还不够成熟。因此
f(I)
,它依赖于索引I
(以及其他无法预计算的值)。
我想填充一个数组a
,以便a[n]=sum(f(I))从I=0到n-1
编辑:在赫里斯托·伊利耶夫发表评论后,我意识到我现在所做的是一件好事
这可以用以下代码编写:
float sum = 0;
for(int i=0; i<N; i++) {
sum += f(i);
a[i] = sum;
}
但是,不使用OpenMP的非并行循环也可以做到这一点。然而,我所提出的解决方案是复杂的,也许还不够成熟。因此,我的问题是,是否有一种更简单、更简单的方法来使用OpenMP实现这一点
下面的代码基本上运行我为每个线程列出的第一个代码。结果是给定线程中a
的值在一个常量内是正确的。我将每个线程的总和保存到带有nthreads+1
元素的数组suma
。这允许我在线程之间通信,并确定每个线程的恒定偏移量。然后我用偏移量校正a[I]
的值
float *suma;
#pragma omp parallel
{
const int ithread = omp_get_thread_num();
const int nthreads = omp_get_num_threads();
const int start = ithread*N/nthreads;
const int finish = (ithread+1)*N/nthreads;
#pragma omp single
{
suma = new float[nthreads+1];
suma[0] = 0;
}
float sum = 0;
for (int i=start; i<finish; i++) {
sum += f(i);
a[i] = sum;
}
suma[ithread+1] = sum;
#pragma omp barrier
float offset = 0;
for(int i=0; i<(ithread+1); i++) {
offset += suma[i];
}
for(int i=start; i<finish; i++) {
a[i] += offset;
}
}
delete[] suma;
float*suma;
#pragma-omp并行
{
const int ithread=omp_get_thread_num();
const int nthreads=omp_get_num_threads();
const int start=ithread*N/nthreads;
常量int finish=(ithread+1)*N/N次读取;
#布拉格omp单曲
{
suma=新浮点[n次读取+1];
suma[0]=0;
}
浮点数和=0;
对于(int i=start;i您可以将策略扩展到任意数量的子区域,并使用任务递归地减少它们:
#include<vector>
#include<iostream>
using namespace std;
const int n = 10000;
const int baseLength = 100;
int f(int ii) {
return ii;
}
int recursiveSumBody(int * begin, int * end){
size_t length = end - begin;
size_t mid = length/2;
int sum = 0;
if ( length < baseLength ) {
for(size_t ii = 1; ii < length; ii++ ){
begin[ii] += begin[ii-1];
}
} else {
#pragma omp task shared(sum)
{
sum = recursiveSumBody(begin ,begin+mid);
}
#pragma omp task
{
recursiveSumBody(begin+mid,end );
}
#pragma omp taskwait
#pragma omp parallel for
for(size_t ii = mid; ii < length; ii++) {
begin[ii] += sum;
}
}
return begin[length-1];
}
void recursiveSum(int * begin, int * end){
#pragma omp single
{
recursiveSumBody(begin,end);
}
}
int main() {
vector<int> a(n,0);
#pragma omp parallel
{
#pragma omp for
for(int ii=0; ii < n; ii++) {
a[ii] = f(ii);
}
recursiveSum(&a[0],&a[n]);
}
cout << n*(n-1)/2 << endl;
cout << a[n-1] << endl;
return 0;
}
#包括
#包括
使用名称空间std;
常数n=10000;
常数int baseLength=100;
内部f(内部ii){
回报二;
}
int recursiveSumBody(int*begin,int*end){
长度=结束-开始;
尺寸=长度/2;
整数和=0;
if(长度<基准长度){
用于(尺寸=1;ii<长度;ii++){
开始[ii]+=开始[ii-1];
}
}否则{
#pragma omp任务共享(总和)
{
sum=递归SumBody(开始,开始+中间);
}
#pragma-omp任务
{
递归SumBody(开始+中间,结束);
}
#pragma omp taskwait
#pragma-omp并行
用于(尺寸=中间;ii<长度;ii++){
开始[ii]+=和;
}
}
返回开始[length-1];
}
void recursiveSum(int*begin,int*end){
#布拉格omp单曲
{
递归SumBody(开始、结束);
}
}
int main(){
向量a(n,0);
#pragma-omp并行
{
#pragma omp for
对于(intii=0;ii cout为完整起见,考虑到Hristo的评论,我添加了OP的MWE代码:
#包括
#包括
使用std::cout;
使用std::endl;
常数int N=10;
常数int Nthr=4;
浮点f(inti){返回(浮点)i;}
内部主(空){
omp_设置_数量_线程(Nthr);
浮动*a=新浮动[N];
浮动*suma=新浮动[Nthr+1];
suma[0]=0.0;
浮动总和=0.0;
#pragma omp并行计划(静态)firstprivate(总和)
for(int i=0;这与OpenMP通常计算前缀和的方式差不多。您可以将#pragma omp for schedule(static)
应用于运行在a[]上的两个循环,而不是手动计算开始和结束索引
@HristoIliev,我认为尽管在实践中OpenMP像我一样定义了开始和结束,但我不应该假设OpenMP会这样做(我想我在你的一篇文章中读到了这一点)
在某些条件下(在您的情况下满足这些条件),具有标准可重复分布模式所保证的特殊属性.好的,我想我明白了。我提出了一个如此多的问题,因为我认为这是其他人可能想知道的。我已经有一段时间不确定了。非常感谢你发布了一个工作示例!我想我希望得到一个适用于OpenMP 2.0的答案(这样它也适用于MSVC)但是这对我来说是一个使用OpenMP任务的好机会。我必须增加baseLength
,以获得n=10000
的正确值。你知道这种方法有多有效吗?嗯,我不认为对于这个特定的示例任务会比你编写的代码更快。我更担心的是你必须增加baseLength
以获得正确的值,这意味着某个地方存在缺陷。无论如何,我无法在程序中看到数据竞争。看来baseLength
必须等于n
才能获得正确的结果。我在我的机器上为任何baseLength
获得正确的结果。Comp我读过g++4.8.1
。奇怪的是,我不知道,我必须包括编译,但就是这样。我使用的是g++4.7.3。
#include<vector>
#include<iostream>
using namespace std;
const int n = 10000;
const int baseLength = 100;
int f(int ii) {
return ii;
}
int recursiveSumBody(int * begin, int * end){
size_t length = end - begin;
size_t mid = length/2;
int sum = 0;
if ( length < baseLength ) {
for(size_t ii = 1; ii < length; ii++ ){
begin[ii] += begin[ii-1];
}
} else {
#pragma omp task shared(sum)
{
sum = recursiveSumBody(begin ,begin+mid);
}
#pragma omp task
{
recursiveSumBody(begin+mid,end );
}
#pragma omp taskwait
#pragma omp parallel for
for(size_t ii = mid; ii < length; ii++) {
begin[ii] += sum;
}
}
return begin[length-1];
}
void recursiveSum(int * begin, int * end){
#pragma omp single
{
recursiveSumBody(begin,end);
}
}
int main() {
vector<int> a(n,0);
#pragma omp parallel
{
#pragma omp for
for(int ii=0; ii < n; ii++) {
a[ii] = f(ii);
}
recursiveSum(&a[0],&a[n]);
}
cout << n*(n-1)/2 << endl;
cout << a[n-1] << endl;
return 0;
}