C++ 简单的基于任务的OpenMP应用程序挂起

C++ 简单的基于任务的OpenMP应用程序挂起,c++,openmp,C++,Openmp,下面的小程序()试图通过递归地划分为四个正方形来计算64×64正方形的面积,直到最小的正方形具有单位长度(几乎不是最优的)。但由于某种原因,程序挂起。我做错了什么 #include <iostream> unsigned compute( unsigned length ) { if( length == 1 ) return length * length; unsigned a[4] , area = 0 , len = length/2; for(

下面的小程序()试图通过递归地划分为四个正方形来计算64×64正方形的面积,直到最小的正方形具有单位长度(几乎不是最优的)。但由于某种原因,程序挂起。我做错了什么

#include <iostream>

unsigned compute( unsigned length )
{
    if( length == 1 ) return length * length;

    unsigned a[4] , area = 0 , len = length/2;

    for( unsigned i = 0; i < 4; ++i )
    {
        #pragma omp task
        {
            a[i] = compute( len );
        }

        #pragma omp single
        {
            area += a[i];
        }
    }

    return area;
}

int main()
{
    unsigned area , length = 64;

    #pragma omp parallel
    {
        area = compute( length );
    }

    std::cout << area << std::endl;
}
#包括
无符号计算(无符号长度)
{
如果(长度==1)返回长度*长度;
无符号a[4],面积=0,长度=长度/2;
for(无符号i=0;i<4;++i)
{
#pragma-omp任务
{
a[i]=计算(len);
}
#布拉格omp单曲
{
面积+=a[i];
}
}
返回区;
}
int main()
{
无符号区域,长度=64;
#pragma-omp并行
{
面积=计算(长度);
}

std::cout单个
构造充当团队中所有线程的隐式屏障。但是,并非团队中的所有线程都会遇到此单个块,因为不同的线程在不同的递归深度下工作。这就是应用程序挂起的原因

在任何情况下,您的代码都不正确。在任务块之后,
a[i]
尚未分配,因此您无法立即使用它!您必须等待任务完成。当然,您不应该在循环内这样做,否则任务分配将无法利用任何并行性。解决方案是在循环结束时这样做。您还必须将
a
指定为共享,以使输出可见:

for( unsigned i = 0; i < 4; ++i )
{
    #pragma omp task shared(a)
    {
        a[i] = compute( len );
    }
}
#pragma omp taskwait
for( unsigned i = 0; i < 4; ++i )
{
    area += a[i];
}

简单地说,这将打开一个包含一组线程的并行区域,并且只有一个线程开始初始计算。其他线程将使用
任务
构造拾取该初始线程随后生成的任务。这就是任务处理的全部内容。

受关于taskwait的讨论以及它如何能够为了避免,我在下面展示了对原始代码稍加修改的版本。请注意,在这种情况下,单个构造末尾的隐含屏障是非常必要的

unsigned tp_area = 0;
#pragma omp threadprivate(tp_area)

void compute (unsigned length)
{
  if (length == 1)
    {
      tp_area += 1;
      return;
    }

  unsigned len = length / 2;

  for (unsigned i = 0; i < 4; ++i)
    {
#pragma omp task
      {
        compute (len);
      }
    }
}

int main ()
{
  unsigned area, length = 64;

#pragma omp parallel
  {
#pragma omp single
    {
      compute (length);
    }
#pragma omp atomic
    area += tp_area;
  }

  std::cout << area << std::endl;
}
unsigned tp_区域=0;
#pragma omp threadprivate(tp_区)
无效计算(无符号长度)
{
如果(长度==1)
{
tp_面积+=1;
返回;
}
无符号长度=长度/2;
for(无符号i=0;i<4;++i)
{
#pragma-omp任务
{
计算(len);
}
}
}
int main()
{
无符号区域,长度=64;
#pragma-omp并行
{
#布拉格omp单曲
{
计算(长度);
}
#布拉格omp原子
面积+=tp_面积;
}

你不能调试你的程序吗?很遗憾我没有。我在设置Eclipse并行调试器(PTP)时遇到了困难。谢谢。程序不再挂起,但我没有得到预期的值4096。我怀疑存在争用情况。您必须将
a
指定为共享。很抱歉,我在最初的帖子中遗漏了这一点。顺便说一句:看一看。他们用类似的示例解释了任务。谢谢。我将看一看幻灯片。我已经学习OpenMP几周了,现在我正在学习我还有很多东西要学。顺便问一下,为什么原子区+=a[i]
在第一个for循环中工作?我想知道是否有可能去掉
#pragma omp taskwait
构造。不可能去掉
taskwait
因为结果需要可用。否则读取
a[I]就没有意义了
。还要确保理解
单个
关键
原子
之间的区别。是的,任务构造内的所有内容(子任务/并行构造除外)都是由单个线程顺序执行的。但是
区域+=a[i]
在任务之外,因此可以由不同的线程执行。在
a[i]=compute(len);
area+=a[i];
之间有一个对
a[i]
的经典读写依赖关系。您可以使
area
共享,并像
tmp=compute(len)一样在线程内移动求和##pragma omp atomic area+=tmp
。然后您需要在
返回区域之前等待
任务;
非常巧妙。而且显然速度也更快。
unsigned tp_area = 0;
#pragma omp threadprivate(tp_area)

void compute (unsigned length)
{
  if (length == 1)
    {
      tp_area += 1;
      return;
    }

  unsigned len = length / 2;

  for (unsigned i = 0; i < 4; ++i)
    {
#pragma omp task
      {
        compute (len);
      }
    }
}

int main ()
{
  unsigned area, length = 64;

#pragma omp parallel
  {
#pragma omp single
    {
      compute (length);
    }
#pragma omp atomic
    area += tp_area;
  }

  std::cout << area << std::endl;
}