OpenMP reduction: min给出不正确的结果



我想在for环外的parallel区域使用OpenMP约简。根据OpenMP参考,在parallel区域可以使用约简子句,因此不需要for环或sections

然而,当在parallel区域使用OpenMPreduction (min:...)时,我得到了不正确的结果。但是,如果我对reduction (max:...)使用完全相同的结构,结果是正确的。如果我使用循环的最小减少(#pragma omp for reduction (min:...)),结果是正确的,但我不认为这应该是必要的。下面是我的代码:

#include <omp.h>
#include <iostream>
#include <limits>
#include <algorithm>
int main(){
auto minVar { std::numeric_limits<int>::max() };
auto maxVar { std::numeric_limits<int>::min() };
auto minVarLoop { std::numeric_limits<int>::max() };
#pragma omp parallel
{
int threadNo { omp_get_thread_num() };
#pragma omp reduction (min:minVar)
minVar = std::min(minVar, threadNo);
// minVar = minVar < threadNo ? minVar : threadNo; // also doesn't work

#pragma omp for reduction(min:minVarLoop)
for (int i=0; i<omp_get_num_threads(); ++i){
minVarLoop = std::min(minVarLoop, threadNo);
}

#pragma omp reduction (max:maxVar)
maxVar = std::max(maxVar, threadNo);
}
std::cout
<<   "min thread num: " << minVar
<< "nmax thread num: " << maxVar
<< "nmin thread num from Loop: " << minVarLoop
<< "n";
return 0;
}

16个线程的预期输出是

min thread num: 0
max thread num: 15
min thread num from Loop: 0
我得到的输出是
min thread num: 12 // or any other number between 0 and 15
max thread num: 15
min thread num from Loop: 0

我在Ubuntu 21.04上使用g++ 10.3.0版本进行编译,只使用-fopenmp标志。

我忽略了什么?

编辑:不是初始化minVar,即使用int minVar;以某种方式使其工作,但我不觉得这是一个令人满意的解决方案。此外,对maxVar进行相同的尝试,使那些结果不正确。哦我。

您正在使用编译器尚未支持的特性。如果你用-Wall编译你的代码,你会看到GCC 10.3.0显示这样的警告:

red.cc:15: warning: ignoring ‘#pragma omp reduction’ [-Wunknown-pragmas]
15 |         #pragma omp reduction (min:minVar)
|
red.cc:24: warning: ignoring ‘#pragma omp reduction’ [-Wunknown-pragmas]
24 |         #pragma omp reduction (max:maxVar)
|

如果你纠正代码,就像我下面展示的使用正确的拼写(参见作用域构造)编译器仍然会反对,因为它还不支持OpenMP API的5.1版本:

red.cc:15: warning: ignoring ‘#pragma omp scope’ [-Wunknown-pragmas]
15 |         #pragma omp scope reduction (min:minVar)
|
red.cc:24: warning: ignoring ‘#pragma omp scope’ [-Wunknown-pragmas]
24 |         #pragma omp scope reduction (max:maxVar)
|

正确的拼写是这样的:

#include <omp.h>
#include <iostream>
#include <limits>
#include <algorithm>
int main(){
auto minVar { std::numeric_limits<int>::max() };
auto maxVar { std::numeric_limits<int>::min() };
auto minVarLoop { std::numeric_limits<int>::max() };
#pragma omp parallel
{
int threadNo { omp_get_thread_num() };
#pragma omp scope reduction (min:minVar)
minVar = std::min(minVar, threadNo);
// minVar = minVar < threadNo ? minVar : threadNo; // also doesn't work

#pragma omp for reduction(min:minVarLoop)
for (int i=0; i<omp_get_num_threads(); ++i){
minVarLoop = std::min(minVarLoop, threadNo);
}

#pragma omp scope reduction (max:maxVar)
maxVar = std::max(maxVar, threadNo);
}
std::cout
<<   "min thread num: " << minVar
<< "nmax thread num: " << maxVar
<< "nmin thread num from Loop: " << minVarLoop
<< "n";
return 0;
}

不同的结果是由于竞争条件。不要问为什么你得到了正确的结果,我每次都得到不同的结果,我运行代码。

你的代码的另一个正确版本(不需要OpenMP API 5.1版本的流血边缘支持)应该是这样的:

#include <omp.h>
#include <iostream>
#include <limits>
#include <algorithm>
int main(){
auto minVar { std::numeric_limits<int>::max() };
auto maxVar { std::numeric_limits<int>::min() };
auto minVarLoop { std::numeric_limits<int>::max() };
#pragma omp parallel reduction(min:minVar) reduction(max:maxVar)
{
int threadNo { omp_get_thread_num() };
minVar = std::min(minVar, threadNo);
#pragma omp for reduction(min:minVarLoop)
for (int i=0; i<omp_get_num_threads(); ++i){
minVarLoop = std::min(minVarLoop, threadNo);
}
maxVar = std::max(maxVar, threadNo);
}
std::cout
<<   "min thread num: " << minVar
<< "nmax thread num: " << maxVar
<< "nmin thread num from Loop: " << minVarLoop
<< "n";
return 0;
}

最新更新