我在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?
}
}
答案 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
是显然是正确的事情。