减少OpenMP并行中的迭代次数

时间:2013-09-16 10:01:28

标签: c++ multithreading openmp

我在C ++程序中有一个parallel for,它必须循​​环到一些迭代次数。每次迭代都会为算法计算一个可能的解决方案,并且我想在找到有效算法后退出循环(如果完成了一些额外的迭代就可以了)。我知道迭代的数量应该从parallel for开始修复,但由于我没有增加以下代码中的迭代次数,是否有任何保证线程在继续执行它们之前检查条件当前的迭代?

void fun()
{
  int max_its = 100;

  #pragma omp parallel for schedule(dynamic, 1)
  for(int t = 0; t < max_its; ++t)
  {
    ...
    if(some condition)
      max_its = t; // valid to make threads exit the for?
  }
}

3 个答案:

答案 0 :(得分:2)

修改循环计数器适用于OpenMP工作共享结构的大多数实现,但程序将不再符合OpenMP,并且无法保证程序可以与其他编译器一起使用。

由于OP可以进行一些额外的迭代,因此OpenMP取消将是最佳选择。 OpenMP 4.0完全为此目的引入了“取消”构造。它将请求终止工作共享构造并将线程传送到它的末尾。

void fun()
{
  int max_its = 100;

#pragma omp parallel for schedule(dynamic, 1)
  for(int t = 0; t < max_its; ++t)
  {
    ...
    if(some condition) {
#pragma omp cancel for
    }
#pragma omp cancellation point for
  }
}

请注意,在性能方面可能需要付出代价,但如果在中止循环时整体性能更好,则可能需要接受此代价。

在OpenMP的4.0之前的实现中,唯一符合OpenMP的解决方案是使if语句尽可能快地接近循环的常规结束而不执行实际的循环体:

void fun()
{
  int max_its = 100;

#pragma omp parallel for schedule(dynamic, 1)
  for(int t = 0; t < max_its; ++t)
  {
    if(!some condition) {
      ... loop body ...
    }
  }
}

希望有所帮助!

干杯,         -Michael

答案 1 :(得分:1)

您无法修改max_its,因为标准规定它必须是循环不变表达式

但是,您可以使用布尔共享变量作为标志:

void fun()
{
  int max_its = 100;
  bool found = false;
  #pragma omp parallel for schedule(dynamic, 1) shared(found)
  for(int t = 0; t < max_its; ++t)
  {
    if( ! found ) {
    ...
    }
    if(some condition) {
  #pragma omp atomic
      found = true; // valid to make threads exit the for?
    }
  }
}

这种逻辑也可以用任务而不是工作共享结构来实现。代码草图如下所示:

void algorithm(int t, bool& found) {
#pragma omp task shared(found)
{
  if( !found ) {
    // Do work
    if ( /* conditionc*/ ) {
      #pragma omp atomic
      found = true
    }
  }
} // task
} // function


void fun()
{
  int max_its = 100;
  bool found  = false;
  #pragma omp parallel 
  {
    #pragma omp single
    {
      for(int t = 0; t < max_its; ++t)
      {
        algorithm(t,found);
      }
    } // single
  } // parallel
}

这个想法是单个线程创建max_its个任务。每个任务都将分配给一个等待的线程。如果某些任务找到了有效的解决方案,那么所有其他任务将通过找到的共享变量得到通知。

答案 2 :(得分:0)

如果some_condition是一个“始终有效”的逻辑表达式,那么您可以这样做:

for(int t = 0; t < max_its && !some_condition; ++t)

这样,很明显继续循环需要!some_condition,并且无需读取其余代码以找出“if some_condition,循环结束”< / p>

否则(例如,如果some_condition是循环内某些计算的结果,并且将some_condition“移动”到for循环条件很复杂,那么使用break是显然是正确的事情。