【问题标题】:OpenMP Parallel Sections Within For Loop (C++) - OverheadFor 循环中的 OpenMP 并行部分 (C++) - 开销
【发布时间】:2019-01-15 03:28:09
【问题描述】:

我一直在研究量子模拟。每个时间步计算一个势函数,迭代求解器的一个步骤,然后进行一系列测量。这三个过程很容易并行化,我已经确保它们不会相互干扰。此外,还有一些相当简单的东西,但不应该并行完成。设置概要如下所示。

omp_set_num_threads(3);
#pragma omp parallel
{
    while (notDone) {
        #pragma omp sections
        {
            #pragma omp section
            {
                createPotential();
            }
            #pragma omp section
            {
                iterateWaveFunction();
            }
            #pragma omp section
            {
                takeMeasurements();
            }
        }
        #pragma omp single
        {
            doSimpleThings();
        }
    }
}

代码运行良好!我看到速度增加,主要与 TDSE 求解器一起运行的测量相关(速度增加约 30%)。但是,该程序从使用大约 10% 的 CPU(大约一个线程)到 35%(大约三个线程)。如果势函数、TDSE 迭代器和测量花费相同的时间,这将是有意义的,但事实并非如此。根据速度的提高,我预计 CPU 使用率大约为 15%。

我感觉这与在 while 循环中运行这三个线程的开销有关。更换

#pragma omp sections

与

#pragma omp parallel sections

(并在循环之前省略两行)没有任何改变。有没有更有效的方法来运行这个设置?我不确定线程​​是否不断被重新创建,或者线程是否在等待其他线程完成时占用了整个核心。如果我将线程数从 3 增加到任何其他数量,程序会使用尽可能多的资源(可能是所有 CPU)并且不会获得性能提升。

【问题讨论】:

  • OpenMP 有时会无缘无故地让线程旋转。但是,我不完全知道是什么原因造成的。鉴于您只是尝试并行运行 3 个函数,您可以尝试使用 STL,我认为像 std::async 这样的东西可以工作。您甚至可以通过给它们一个返回值并将其绑定到 std::future 对象来让您的主线程等待 3 完成。
  • 我认为单个语句存在障碍,因此其他线程应该休眠。我也会调查OMP_WAIT_POLICYstackoverflow.com/a/12617270/2542702
  • 也许这是部分的问题,并且可能是使用任务的原因。您是否尝试过使用任务而不是部分?在 OpenMP 3.0 中添加任务之前,部分对我来说似乎是旧的做事方式。
  • 您可以尝试#pragma omp sections nowait,如果需要,可以添加#pragma omp barrier。

标签: c++ multithreading openmp sections


【解决方案1】:

我尝试了许多选项,包括使用任务而不是部分(结果相同)、切换编译器等。正如 Qubit 所建议的,我也尝试使用 std::async。这就是解决方案! CPU 使用率从大约 50% 下降到 30%(这是在与原始帖子不同的计算机上,所以数字不同——基本上 1.6 倍 CPU 使用率获得了 1.5 倍的性能提升)。这更接近我对这台计算机的预期。

作为参考,这里是新的代码大纲:

void SimulationManager::runParallel(){
    auto rV = &SimulationManager::createPotential();
    auto rS = &SimulationManager::iterateWaveFunction();
    auto rM = &SimulationManager::takeMeasurements();
    std::future<int> f1, f2, f3;
    while(notDone){
        f1 = std::async(rV, this);
        f2 = std::async(rS, this);
        f3 = std::async(rM, this);
        f1.get(); f2.get(); f3.get();
        doSimpleThings();
    }
}

使用 std::async 调用三个原始函数,然后我使用未来变量 f1、f2 和 f3 将所有内容收集回单个线程并避免访问问题。

【讨论】:

  • 您使用了哪些编译器选项?什么操作系统?什么编译器?硬件是什么?
  • 这是在 Windows 10 上。我尝试了 Visual Studio 2017 以及 Intel C++(与 VS 集成)编译器。 VS 内置了很多编译器选项,您在寻找特定的吗?在硬件方面,我的笔记本电脑有一个 i7-4710 HQ,2.5 GHz - 不确定是否有任何其他规格对于这种情况来说太重要了。
  • 可能最重要的是操作系统。您可能会在 Linux 上看到不同的结果(例如,由于不同的线程调度程序)。 MSVC 的 OpenMP 实现非常古老,但 ICC 很好,所以自从您尝试了 ICC,我认为编译器偏差的可能性较小。但在更基本的层面上,两种 OpenMP 实现都基于 Windows 线程而不是 pthread。无论如何,你的问题和答案很有趣。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-31
  • 1970-01-01
  • 2012-05-19
  • 1970-01-01
  • 2016-07-23
  • 1970-01-01
  • 2022-01-19
相关资源
最近更新 更多