c++ - openmp嵌套循环处理性能

标签 c++ performance openmp

请考虑以下代码:

void process(int N, int K, const vector<int>& data)
{
    #pragma omp parallel for
    for(int i = 0; i < data.size(); ++i)
    {
        //perform some processing based on data, N and K
    }
}

void solve(int N, int K, const vector<int>& data)
{
    for(int i = 0; i < N; ++i)
    {
        for(int j = 0; j < K; ++j)
        {
            process(N, K, data);
        }
    }
}

上面的代码是通过每个参数的不同大小来执行的。 N 和 K 的范围为 1 - 1000(大多数情况下)。很多时候两者都是 1。 data.size() 也变化很大,在 100 到 300 000 之间。

上面的代码在大多数情况下效果都很好。问题是 N 或 K 是否大于~100。例如K为300,数据并没有那么大。例如:1000。 在这种情况下,大多数时间我的程序都在等待唤醒 omp 线程。如果我禁用 omp,那么在这种情况下程序速度会快 2-3 倍。

我的问题是 - 是否有可能以某种方式检测 omp 以在求解函数内执行循环时保持自旋锁? 我已经尝试过 OMP_WAIT_POLICY Active 并且它解决了问题,但由于其他原因(它只是大型应用程序的一小部分),到目前为止我必须保持被动模式。是否有其他选项可以使线程保持事件状态一段时间(或任何其他想法如何解决此问题)?

编辑: 根据 @Gilles 的说法,这是我的完整测试程序:

#include <atomic>
#include <iostream>
#include <vector>
#include <chrono>
#include <omp.h>

std::atomic<int> cnt;

void process(int a, int b, std::vector<int>& d)
{
    #pragma omp parallel for 
    for (int i = 0; i < d.size(); ++i)
    {
        //sample operation
        if (d[i] > a + b)
            ++cnt;
    }
}

void solve(int N, int K, std::vector<int>& d)
{
    for (int i = 0; i < N; ++i)
    {
        for (int j = 0; j < K; ++j)
        {
            process(i, j, d);
        }
    }
}

void RunTest(int numOfThreads, int N, int K, int arrSize)
{
    std::vector<int> s(arrSize);
    s[0] = s[10] = 1000;

    omp_set_num_threads(numOfThreads);
    cnt = 0;

    std::chrono::duration<double> minDiff = std::chrono::duration<double>{ 99999999 };
    for (int iters = 0; iters < 20; ++iters)
    {
        auto start = std::chrono::high_resolution_clock::now();
        solve(N, K, s);
        auto end = std::chrono::high_resolution_clock::now();

        std::chrono::duration<double> diff = end - start;
        if (diff < minDiff)
            minDiff = diff;
    }
    std::cout << "Time: " << minDiff.count() * 1000 << " ms \t\t" << "Threads: " << numOfThreads << " N: " << N << " K: " << K << std::endl;
}

int main()
{
    std::cout << "Large N*K" << std::endl;
    RunTest(6, 100, 100, 10000);
    RunTest(1, 100, 100, 10000);

    std::cout << std::endl;
    std::cout << "Small N*K" << std::endl;
    RunTest(6, 1, 1, 1000000);
    RunTest(1, 1, 1, 1000000);
}

根据 ACTIVE/PASSIVE 等待策略的结果(在 MSVC 2019 上测试):

PASSIVE:
Large N*K
Time: 126.358 ms                Threads: 6 N: 100 K: 100
Time: 83.0023 ms                Threads: 1 N: 100 K: 100

Small N*K
Time: 0.194 ms                  Threads: 6 N: 1 K: 1
Time: 0.6687 ms                 Threads: 1 N: 1 K: 1

ACTIVE
Large N*K
Time: 20.8449 ms                Threads: 6 N: 100 K: 100
Time: 82.4809 ms                Threads: 1 N: 100 K: 100

Small N*K
Time: 0.1404 ms                 Threads: 6 N: 1 K: 1
Time: 0.6845 ms                 Threads: 1 N: 1 K: 1

正如您在被动模式中所看到的,当 N*K 很大时,时间会更长。

最佳答案

or any other idea how to fix this issue?

将计算分配给线程时,您希望拥有尽可能大的 block 和尽可能少的同步。在您的示例中,您应该并行化最外层循环。在您的示例中,不清楚 process 是否修改 data。它作为非常量传递,但假设它没有被修改,这是我期望表现更好的东西:

void solve(int N, int K, vector<int>& data)
{
    #pragma omp parallel for
    for(int i = 0; i < N; ++i)
    {
        for(int j = 0; j < K; ++j)
        {
            process(N, K, data);
        }
    } // <-- threads have to wait here until all are finished
}

(简单化)基本原理:生成和收集线程需要时间并引入开销。在您的代码中,您的开销是 N*K 倍。如果并行化最外面的循环,您就会有一次这样的开销。

关于c++ - openmp嵌套循环处理性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58431603/

相关文章:

c++ - 你能在 C++ 中保护嵌套类吗?

javascript - node.js:嵌套for循环,字符串操作性能惨淡

c++ - OpenMP 代码中止

mac OS X El Capitan 上的 C++ openmp,Valgrind 错误(数据竞争)

并行任务中的 C++ OpenMP 变量可见性

c++ - 局部表面拟合到 3d 点

c++ - 将结构转换为字符数组

c++ - pugixml:找出 xpath 是否与特定节点/属性匹配

performance - 衡量 MPI 通信成本的工具

java - 标准阿克曼可以优化吗?