c++ - 当另一个进程正在运行时,OpenMP 非常慢

标签 c++ multithreading openmp

当尝试在 C++ 应用程序中使用 OpenMP 时,我遇到了严重的性能问题,其中多线程性能可能比单线程性能差 1000 倍。仅当至少一个核心被另一进程耗尽时才会发生这种情况。

经过一番挖掘,我可以将问题隔离为一个小示例,我希望有人能够阐明这个问题!

最小示例

这是一个说明问题的最小示例:

#include <iostream>

int main() {
    int sum = 0;
    for (size_t i = 0; i < 1000; i++) {
        #pragma omp parallel for reduction(+:sum)
        for (size_t j = 0; j < 100; j++) {
            sum += i;
        }
    }
    
    std::cout << "Sum was: " << sum << std::endl;
}

我需要 OpenMP 指令位于外部 for 循环内部,因为我的实际代码是在相互依赖的时间步上循环的。

我的设置

我在使用 AMD Ryzen 9 5900X(12 核、24 线程)的 Ubuntu 21.04 上运行了该示例,并使用 g++ -fopenmp example.cc 使用 G++ 10.3.0 对其进行了编译。

基准测试

如果您在后台没有任何其他内容的情况下运行该程序,它会很快终止:

> time ./a.out
Sum was: 999000

real    0m0,006s
user    0m0,098s
sys     0m0,000s

但是,如果另一个进程使用单个核心,它的运行速度会非常慢。在本例中,我运行 stress -c 1 来模拟另一个完全使用后台核心的进程。

> time ./a.out
Sum was: 999000

real    0m8,060s
user    3m2,535s
sys     0m0,076s

速度减慢了 1300 倍。我的机器有 24 个并行线程,因此当其中一个线程繁忙且另外 23 个线程可用时,理论上的速度减慢应该仅为 4% 左右。

调查结果

问题似乎与 OpenMP 如何分配/指派线程有关。

  • 如果我将 omp 指令移至外循环,问题就会消失
  • 如果我明确将线程数设置为 23,问题就会消失 (num_threads(23))
  • 如果我明确将线程数设置为 24,问题仍然存在
  • 进程终止所需的时间从 1 到 8 秒不等
  • 程序在运行时不断使用尽可能多的 CPU,我假设大多数 OpenMP 线程都处于自旋锁中

从这些发现来看,OpenMP 似乎将作业分配给所有核心,包括已经达到极限的核心,然后以某种方式强制每个核心完成其任务,并且不允许在其他核心完成时重新分配它们.

我尝试将计划更改为动态,但这也没有帮助。

如果有任何建议,我都会非常有帮助,我是 OpenMP 新手,所以我可能犯了一个错误。你对此有何看法?

最佳答案

所以这是我能弄清楚的:

使用 OMP_DISPLAY_ENV=verbose 运行程序(有关环境变量列表,请参阅 https://www.openmp.org/spec-html/5.0/openmpch6.html)

详细设置将显示 OMP_WAIT_POLICY = 'PASSIVE'GOMP_SPINCOUNT = '300000'。换句话说,当一个线程必须等待时,它会自旋一段时间然后进入休眠状态,消耗CPU时间并阻塞一个CPU。每次线程到达循环末尾时或在主线程分发 for 循环之前,甚至可能在并行部分开始之前,都会发生这种情况。

由于 GCC 的 libgomp 不使用 pthread_yield,这实际上会阻塞一个 CPU 线程。由于正在运行的软件线程多于 CPU 线程,因此其中一个线程不会运行,导致所有其他线程忙等待,直到内核调度程序重新分配 CPU。

如果您使用 OMP_WAIT_POLICY=passive 调用程序,GCC 将设置 GOMP_SPINCOUNT = '0'。然后内核将立即将等待线程置于 sleep 状态并允许其他线程运行。现在你的表现将会好得多。

有趣的是OMP_PROC_BIND=true也有帮助。我认为不可移动的线程会以某种方式影响内核调度程序,这对我们有利,但我不确定。

Clang 的 OpenMP 实现不会受到这种性能下降的影响,因为它使用 pthread_yield。当然,如果系统调用开销很大,并且在大多数计算环境中,这有其自身的缺点,那么它应该是不必要的,因为您不应该过度使用 CPU。

关于c++ - 当另一个进程正在运行时,OpenMP 非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70126350/

相关文章:

c++ - 升压::精神::莱克斯;如何指定 token "||"?

c++ - 为什么不编译? (VC++ 2015,#pragma omp flush)

c++ - 打开 mp 3 for 循环减少

c++ - C++ 中的内存黑客攻击/修改

.net - 合并两个图像,其中一个是透明的

iphone - NSTimer 和更新 UI

创建多线程进行平方根运算

c# - 如何创建一个总是产生的任务?

c++ - vector 乘法中的 SIMD 与 OMP

c++ - 指向实例成员的函数指针