背景:
我阅读了一些关于OpenCV中多线程的文章和帖子:
- 一方面,您可以使用TBB或OpenMP支持构建OpenCV,这会在内部并行化OpenCV的函数。
- 另一方面,您可以自己创建多个线程,并调用函数以实现应用程序级别的多线程。
但我无法得到一致的答案,哪种多线程方法是正确的。
关于TBB,来自2012年的答案获得了5个赞:
使用WITH_TBB = ON,OpenCV尝试为某些函数使用多个线程。问题在于,目前只有少量函数使用TBB进行线程处理(可能有十几个)。因此,很难看到任何加速。 OpenCV的理念是应用程序应该是多线程的,而不是OpenCV函数。[...]
关于应用程序级别的多线程,answers.opencv.org上的一个评论来自一位主持人:
请避免在opencv中使用自己的多线程。许多函数明确不支持多线程。 而是使用TBB或openmp支持重新构建opencv库。
但是另一个答案得到了3个赞同,它说:
这个库本身是线程安全的,因为你可以同时对该库进行多次调用,但是数据并不总是线程安全的。
问题描述:
所以我认为在应用程序级别上使用(多)线程至少是可以的。但是当我运行我的程序一段时间后,遇到了奇怪的性能问题。
在调查这些性能问题后,我创建了这个最小、完整和可验证的示例代码:
#include "opencv2\opencv.hpp"
#include <vector>
#include <chrono>
#include <thread>
using namespace cv;
using namespace std;
using namespace std::chrono;
void blurSlowdown(void*) {
Mat m1(360, 640, CV_8UC3);
Mat m2(360, 640, CV_8UC3);
medianBlur(m1, m2, 3);
}
int main()
{
for (;;) {
high_resolution_clock::time_point start = high_resolution_clock::now();
for (int k = 0; k < 100; k++) {
thread t(blurSlowdown, nullptr);
t.join(); //INTENTIONALLY PUT HERE READ PROBLEM DESCRIPTION
}
high_resolution_clock::time_point end = high_resolution_clock::now();
cout << duration_cast<microseconds>(end - start).count() << endl;
}
}
实际行为:
如果程序运行了一段时间,{{打印出的时间跨度}}会变得很长。
cout << duration_cast<microseconds>(end - start).count() << endl;
越来越大。
在运行程序约10分钟后,打印的时间跨度加倍了,这不能用正常波动来解释。
期望的行为:
我期望程序的行为是时间跨度基本保持不变,即使它们可能比直接调用函数的时间跨度长。
注意事项:
直接调用函数时:
[...]
for (int k = 0; k < 100; k++) {
blurSlowdown(nullptr);
}
[...]
打印的时间跨度保持不变。
在不调用cv函数时:
void blurSlowdown(void*) {
Mat m1(360, 640, CV_8UC3);
Mat m2(360, 640, CV_8UC3);
//medianBlur(m1, m2, 3);
}
打印的时间跨度也保持不变。因此,在将线程与OpenCV函数组合使用时,必须出了问题。
- 我知道上面的代码并没有实现真正的多线程,只会有一个线程在同时调用
blurSlowdown()
函数。 - 我知道创建线程和清理它们后来不是免费的,并且比直接调用函数要慢。
- 这不是关于代码总体上慢的问题。问题在于打印的时间跨度随着时间的推移越来越长。
- 问题与
medianBlur()
函数无关,因为它也会发生在其他函数(如erode()
或blur()
)中。 - 该问题在Mac下的clang++下复制,详情请参见@Mark Setchell的评论。
- 使用调试库而不是发布库可以放大该问题。
我的测试环境:
- Windows 10 64位
- MSVC编译器
- 官方OpenCV 3.4.2二进制文件
我的问题:
- 在OpenCV的应用程序级别使用多线程是否可行?
- 如果可以,为什么我的程序打印的时间跨度随着时间的推移增长?
- 如果不行,那么为什么OpenCV被认为是线程安全的,请解释如何解释来自Kirill Kornyakov的声明。
- 2019年TBB / OpenMP现在被广泛支持吗?
- 如果可以,多线程应用程序级别(如果允许)和TBB / OpenMP哪个提供更好的性能?
std::vector
,在循环中填充它,并在循环外部加入所有线程。或者使用std::future
和std::promise
。 - πάντα ῥεῖmedianBlur
只分配 mats 时,时间保持恒定。所以我认为仅有线程并不是问题。此外,当使用调试二进制文件而非发布二进制文件时,时间跨度会增长得更快。 - Crigges