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

时间:2016-07-19 13:43:37

标签: c++ openmp

以下小程序(online version)尝试通过递归地划分为四个正方形来计算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;
}

2 个答案:

答案 0 :(得分:2)

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

在任何情况下,您的代码都不正确。在您的任务块之后,尚未分配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];
}

请注意,缩减不包含single构造!计算由任务执行,因此只有一个线程应该拥有它自己的本地area。但是,在首次生成任何任务之前,您需要一个single构造:

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

简单地说,这会打开一个带有一组线程的并行区域,只有一个线程开始初始计算。其他线程将获取稍后由具有task构造的初始线程生成的任务。这就是任务的全部。

答案 1 :(得分:1)

关于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;
}