2

我有parallel for一个 C++ 程序,它必须循​​环到一定数量的迭代。每次迭代都会为算法计算一个可能的解决方案,一旦找到有效的解决方案,我想退出循环(如果完成了一些额外的迭代就可以了)。我知道迭代次数应该从头开始固定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?
  }
}
4

3 回答 3

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 ...
    }
  }
}

希望有帮助!

干杯,-迈克尔

于 2013-09-16T20:09:52.677 回答
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任务。每个任务将被分配给一个等待线程。如果某些任务找到了有效的解决方案,那么所有其他任务都将通过找到的共享变量得到通知。

于 2013-09-16T10:44:15.617 回答
0

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

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

这样,就很清楚!some_condition需要继续循环了,不需要再看剩下的代码就知道“如果some_condition,循环结束”

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

于 2013-09-16T10:13:10.923 回答