基本上,我有一个并行循环,其中有几个我预先计算的固有串行变量。然而,我希望能够在计算出串行变量后启动 for 循环的给定迭代。 本质上我想要这样的东西:
int num_completed = 0;
int serial_values[100];
#pragma omp parallel
{
if(omp_get_thread_num() == 0){
for(int i = 0; i < 100; i++){
serial_values[i] = i;
num_completed++;
}
}
#pragma omp for
for(int j = 0; j < 100; j++){
while(true){
if(j < num_completed)
break;
}
int serial = serial_values[j]
do parallel loop
}
}
这确实有效,但 for 循环将工作分配给线程 0,即使它与串行计算有关。大多数情况下,这意味着它会更慢,因为除了串行变量之外,线程 0 还必须计算其在并行循环中的份额。
我也知道自旋锁不是很好,但我想不出更好的东西,如果你有建议,我也欢迎。
我已经尝试过使用
#pragma omp single nowait
,它的功能是一样的。我也尝试过 #omp section
但每个部分都是由单个线程执行,而不是并行执行。
您已经在使用自旋锁来使并行循环中的线程等待串行计算进行得足够远。 前进的一种方法是利用它,加上 OpenMP 调度参数,使并行循环中的线程执行串行计算。 可能看起来像这样:
int num_completed = 0;
int serial_values[100];
#pragma omp parallel for schedule(monotonic:static, 1)
for (int j = 0; j < 100; j++){
int serial;
while (1) {
if (j == num_completed) {
// serial computation:
serial = j
serial_values[j] = serial;
num_completed += 1;
break;
}
}
// do parallel loop
}
schedule(monotonic:static, 1)
在这里相对重要,因为它确保并行循环的迭代在单迭代块中的线程之间分割,并且每个线程按逻辑迭代顺序执行其分配的块。 您还可以使用 schedule(monotonic:dynamic)
或等效的 schedule(monotonic:dynamic, 1)
。 如果块大于一次迭代,线程可能会延迟不必要的长时间(这可能是原始代码的问题)。 如果块不按顺序执行,您可能会在这些自旋锁上遇到死锁。
我也尝试过
,但每个部分都应该由单个线程执行,而不是并行执行。#omp section
是的,但您可以在其中一个部分内放置一个嵌套的
parallel
区域。 我认为这可能不如上面描述的方法,但它可以产生更类似于原始代码的结构。 例如这样的事情:
int num_completed = 0;
int serial_values[100];
#pragma omp parallel num_threads(2)
{
#pragma omp sections
{
#pragma omp section
{
for(int i = 0; i < 100; i++){
serial_values[i] = i;
num_completed++;
}
}
#pragma omp section
{
#pragma omp for schedule(monotonic:static, 1)
for(int j = 0; j < 100; j++){
while(true){
if(j < num_completed)
break;
}
int serial = serial_values[j]
// do parallel loop ...
}
}
}
}
如果愿意,您也可以在并行
num_threads()
循环上放置一个 for
子句。