由于 OpenMP 2.0 没有取消构造,因此您必须自己实现一个取消构造,例如通过使用共享变量。这也意味着您无法使用for
不允许使用工作共享构造来打破并行循环(这就是 OpenMP 4.0 引入取消构造的原因)。如果您在每个元素的评估之间实施取消检查,则可能会出现两个或多个线程找到与条件匹配的元素的情况。因此,您应该对索引执行最小缩减:
int found = 0;
int first_index = INVALID_VALUE;
int iteration = 0;
#pragma omp parallel
{
int my_index = INVALID_VALUE;
int i;
do
{
// Later versions of OpenMP allow for "atomic capture"
// but OpenMP 2.0 requires a critical directive instead
#pragma omp critical(iteration)
{
i = iteration++;
}
if (i < N && check(i))
{
found = 1;
my_index = i;
}
} while (!found && i < N);
#pragma omp critical(reduction)
if (my_index != INVALID_VALUE)
{
if (first_index == INVALID_VALUE || my_index < first_index)
first_index = my_index;
}
// Only needed if more code follows before the end of the region
#pragma omp barrier
...
}
此代码假设检查第 i 个元素的条件 (check(i)
)不会改变元素的状态,因此,可能发生的最坏情况是找到匹配元素的线程可能必须等待所有其他线程完成检查它们当前正在处理的元素,并且等待时间将是所有处理时间中的最大值。
The critical
do 循环中使用的构造非常昂贵。如果check()
不需要那么多时间,那么您可能会考虑使用块而不是迭代:
do
{
#pragma omp critical(chunk)
{
my_chunk = chunk++;
}
if (my_chunk >= N_chunks)
break;
for (i = my_chunk * chunk_size; !found && i < (my_chunk+1)*chunk_size; i++)
{
if (check(i))
{
found = 1;
my_index = i;
break;
}
}
} while (!found && my_chunk < N_chunks);
当元素数量不是很大并且检查每个元素的成本很高时,另一种解决方案效果相当好:
#pragma omp parallel
{
#pragma omp for schedule(dynamic,x)
for (i = 0; i < N; i++)
{
if (!found && check(i))
{
my_index = i;
found = 1;
}
}
// Min reduction from the solution above
...
}
Once found
为 true 时,其余的循环迭代将运行“空”体,因为&&
.