繁体   English   中英

使用 std::thread 时与 g++/OpenMP 相关的错误?

[英]Bug related to g++/OpenMP when using std::thread?

我已经将我遇到的问题提炼为它的基本要素。 这是第一个示例代码:

#include <vector>
#include <math.h>
#include <thread>

std::vector<double> vec(10000);

void run(void) 
{
    for(int l = 0; l < 500000; l++) {

    #pragma omp parallel for
        for(int idx = 0; idx < vec.size(); idx++) {

            vec[idx] += cos(idx);
        }
    }
}

int main(void)
{
    #pragma omp parallel
    {
    }

    std::thread threaded_call(&run);
    threaded_call.join();

    return 0;
}

将其编译为(在 Ubuntu 20.04 上): g++ -fopenmp main.cpp -o main

编辑:版本:g++(Ubuntu 9.3.0-17ubuntu1~20.04)9.3.0

在 Ryzen 3700x(8 核,16 线程)上运行:运行时间 ~ 43秒,系统监视器中报告的所有 16 个逻辑核心都在 ~ 80%

接下来取出#pragma omp parallel指令,那么main函数就变成了:

int main(void)
{
    std::thread threaded_call(&run);
    threaded_call.join();

    return 0;
}

现在运行时间 ~ 9 秒,系统监视器中报告的所有 16 个逻辑内核都为100%

我还在 Windows 10 上使用 MSVC 编译了这个,无论#pragma omp 并行指令是否存在,cpu 利用率始终为 ~100%。 是的,我完全知道这条线应该什么都不做,但是使用 g++ 它会导致上述行为; 也只有在线程上调用 run 函数时才会发生,而不是直接调用。 我尝试了各种编译标志(-O 级别),但问题仍然存在。 我想下一步是查看汇编代码,但我看不出这只是 g++ 中的一个错误。 任何人都可以对此有所了解吗? 将不胜感激。

此外,调用 omp_set_num_threads(1); 在循环之前的“void run(void)”函数中,为了检查单个线程需要多长时间,只有一个线程 100%(如预期)提供了大约70秒的运行时间。

此外,可能相关的问题(虽然这可能是我缺乏理解):调用 omp_set_num_threads(1); 在“int main(void)”函数中(在定义 threaded_call 之前)用 g++ 编译时什么都不做,即所有 16 个线程仍然在 for 循环中执行,而不管虚假的 #pragma omp 并行指令。 使用 MSVC 编译时,这只会导致一个线程按预期运行 - 根据 omp_set_num_threads 的文档,我认为这应该是正确的行为,但对于 g++ 则不然。 为什么不呢,这是另一个错误吗?

编辑:我现在理解的最后一个问题( 从代码中覆盖 OMP_NUM_THREADS - 真正的),但仍然使原始问题悬而未决。

感谢 Hristo Iliev 的有用评论,我现在明白了这一点,并想回答我自己的问题,以防万一它对任何有类似问题的人有用。

问题是,如果在主程序线程中执行任何 OpenMP 代码,其状态将变为“污染”——特别是在“#pragma omp parallel”指令之后,OpenMP 线程保持忙碌状态(全部 16 个),这会影响性能任何 std::thread 线程中的所有其他 OpenMP 代码,它们产生自己的 OpenMP 线程团队。 由于主线程仅在程序完成时超出范围,因此整个程序执行过程中都会存在此性能问题。 因此,如果将 OpenMP 与 std::thread 一起使用,请确保主程序线程中绝对不存在 OpenMP 代码。

为了证明这一点,请考虑以下修改后的示例代码:

#include <vector>
#include <math.h>
#include <thread>

std::vector<double> vec(10000);

void run(void) 
{
    for(int l = 0; l < 500000; l++) {

    #pragma omp parallel for
        for(int idx = 0; idx < vec.size(); idx++) {

            vec[idx] += cos(idx);
        }
    }
}

void state(void)
{
#pragma omp parallel
    {
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(5000));
}

int main(void)
{
    std::thread state_thread(&state);
    state_thread.detach();

    std::thread threaded_call(&run);
    threaded_call.join();

    return 0;
}

此代码在前 5 秒内以 80% 的 CPU 使用率运行,然后在程序运行期间以 100% 的 CPU 使用率运行。 这是因为在第一个 std::thread 中产生了一个由 16 个 OpenMP 线程组成​​的团队并保持忙碌状态,从而影响了第二个 std::thread 中 OpenMP 代码的性能。 一旦第一个 std::thread 终止,第二个 std::thread 的性能就不再受到影响,因为第二个 16 个 OpenMP 线程团队现在不必与第一个竞争 CPU 访问。 当有问题的代码在主线程中时,问题一直持续到程序结束。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM