使用OpenCV时的OpenMP和显式线程互操作性

时间:2017-07-23 01:06:29

标签: multithreading opencv parallel-processing openmp threadpool

我在商业应用程序中使用OpenCV,并且没有购买TBB许可的管理权限,因此我使用OpenMP作为并行框架构建了OpenCV。

我们用作实时处理的帧源的所有机器视觉相机都有SDK,它们在循环队列中填充帧缓冲区,并使用数据和调用用户提供的回调来同时处理它们的线程。软件开发工具包'自己的线程池。

在不考虑OpenMP的情况下这很好用,因为我在对各个帧进行一系列(无记忆)处理之前,通过串行缓冲来提供缓冲区,以便为需要按顺序处理帧的状态处理阶段提供信息。如果它只是并发帧处理,那么我根本不需要OpenMP;但是,我需要在OpenCV中启用它,以便加速有序帧处理。

我担心的是,当第一阶段使用OpenMP时,我可以期待OpenMP能够正常工作,相机SDK明确创建的线程中同时执行的回调。当在多个外部创建的线程中触发并行区域时,我是否可以假设OpenMP运行时足够智能以高效的方式使用其线程池?

平台保证为x86-64(VC ++ 15或GCC)。

2 个答案:

答案 0 :(得分:1)

<强>场合

如果我已经正确理解了这个问题,那么你正在使用的相机库会产生许多线程,并且每个线程都会调用你的回调函数。在回调中,您希望使用OpenMP来加速处理。其结果通过一些线程通道发送到线程管道进行更多处理。

如果这是错的,请忽略此答案的其余部分!

其余的答案

在回调中使用OpenMP似乎会将应用程序的这部分计算负载削减成小部分而没有太大的好处。相机库已经与帧的处理重叠。在这里使用OpenMP意味着帧的处理实际上并不重叠(但是相机库仍然使用多个线程,就像它一样)。

如果 仍然重叠,那么从逻辑上讲,您的系统中没有足够的核心来跟上总体工作负载(假设您使用OpenMP导致所有核心被最大化处理单个帧)...我假设你的系统成功地跟上帧的流动,并且必须有足够的咕噜声才能够这样做。

所以,我认为OpenMP在使用它的线程池时会不会是一个聪明的问题。线程池将专用于处理单个帧,它将在下一帧到达之前完成。

非过度拍摄确实意味着延迟较低,这可能是您想要的。但是,如果相机库使用OpenMP使用单个线程进行回调(并且在下一帧到达之前承担责任),则可以实现相同的目的。随着更少的线程上下文切换,它甚至会更快一点。因此,如果您可以停止生成所有这些线程的库(可能有配置参数,环境变量或其API的其他部分),那么它可能是值得的。

答案 1 :(得分:0)

以下是一些示例代码,解释了我找到的解决方法。 LibraryFunction()表示我无法修改已经使用OpenMP并行化的一些函数,例如来自OpenCV的函数。

void LibraryFunction(int phase, mutex &mtx)
{
    #pragma omp parallel num_threads(3)
    {
        lock_guard<mutex> l{mtx};
        cerr << "Phase: " << phase << "\tTID: " << this_thread::get_id() << "\tOMP: " << omp_get_thread_num() << endl;
    }
}

使用外部线程超额认购的问题可见:

int main(void)
{
    omp_set_dynamic(thread::hardware_concurrency());
    omp_set_nested(0);
    vector<std::thread> threads;
    threads.reserve(3);
    mutex mtx;
    for (int i = 0; i < 3; ++i)
    {
        threads.emplace_back([&]
        {
            this_thread::sleep_for(chrono::milliseconds(200));
            LibraryFunction(1, mtx);
        });
    }
    for (auto &t : threads) t.join();
    cerr << endl;
    LibraryFunction(2, mtx);
    return EXIT_SUCCESS;
}

输出结果为:

Phase: 1        TID: 7812       OMP: 0
Phase: 1        TID: 3928       OMP: 0
Phase: 1        TID: 2984       OMP: 0
Phase: 1        TID: 9924       OMP: 1
Phase: 1        TID: 9560       OMP: 2
Phase: 1        TID: 2576       OMP: 1
Phase: 1        TID: 5380       OMP: 2
Phase: 1        TID: 3428       OMP: 1
Phase: 1        TID: 10096      OMP: 2

Phase: 2        TID: 9948       OMP: 0
Phase: 2        TID: 10096      OMP: 1
Phase: 2        TID: 3428       OMP: 2

阶段1表示在相机SDK线程中执行OpenMPed库代码,而阶段2是OpenMPed库代码,用于稍后处理仅从一个线程触发的管道阶段。问题很明显 - 线程数乘以OpenMP-in-external线程的嵌套,并在第1阶段导致超额预订。建立OpenCV并关闭OpenMP,同时修复第1阶段的超额订阅,另一方面结果没有加速第2阶段。

我发现的解决方法是在LibraryFunction()的阶段1期间将调用包装到#pragma omp parallel sections {}会抑制在该特定调用中生成线程。结果是:

Phase: 1        TID: 3168       OMP: 0
Phase: 1        TID: 8888       OMP: 0
Phase: 1        TID: 5712       OMP: 0

Phase: 2        TID: 10232      OMP: 0
Phase: 2        TID: 5012       OMP: 1
Phase: 2        TID: 4224       OMP: 2

我还没有用OpenCV测试过这个,但我希望它可以正常工作。