OpenMP 缩减:min 给出不正确的结果

OpenMP reduction: min gives incorrect result

我想在 parallel 区域、for 循环之外使用 OpenMP 缩减。根据 OpenMP reference,可以在 parallel 区域使用缩减子句,因此不需要 for 循环或 sections

但是,在 parallel 区域使用 OpenMP reduction (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 进行相同的操作会使 those 结果不正确。天啊

您正在使用编译器尚不支持的功能。如果您使用 -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)
      |

如果您更正代码,就像我在下面显示的那样使用正确的拼写(参见 scope Construct),编译器仍然会反对,因为它不支持 OpenMP 5.1 版 API还有:

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 5.1 API 版的前沿支持)是这样的:

#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;
}