在OpenCV中访问具有多个线程的Mat是否安全?

时间:2019-08-08 20:40:09

标签: c++ opencv parallel-processing

我想加速一种算法(具有圆邻的完整局部二进制模式),为此我遍历所有像素并计算与它相邻的东西(因此我需要相邻像素访问)。

目前,我是通过一个线程/进程遍历所有像素来实现的。我想通过将输入图像分为多个ROI并分别计算每个ROI(使用多个线程)来并行化此任务。

这里的问题是,ROI重叠(因为要计算像素,有时我需要查看远处的邻居),并且可能有多个线程访问Pixel-Data( READING )在同一时间。如果两个或多个线程同时读取相同索引的Mat,这是否有问题?

如果我以相同的并行度但以不同的索引写入同一Mat,这也是一个问题吗?

2 个答案:

答案 0 :(得分:2)

通常,并行读取不是问题,因为cv::Mat就像是std::vector一样,是数组周围的一个很好的包装器(是的,但有差异,但我看不到它们如何影响问题在这里,所以我将忽略它们)。但是,并行化并不会自动提高性能。这里有很多事情要考虑:

创建线程会占用大量资源,并且如果任务相对较短(在计算时间方面),可能会产生很大的负面影响,因此必须考虑线程池化。

如果编写高性能代码(无论是多线程还是单线程),您都应该掌握硬件的工作方式。在这种情况下:内存和CPU。在2016年CppCon大会上,帖木儿·杜姆勒(Timur Doumler)发了very good talk主题。这应该可以帮助您避免缓存未命中。

值得一提的是编译器优化。打开它。我知道这听起来很明显,但是SO上有很多人在询问有关性能的问题,但他们不知道什么是编译器优化。

最后,有一个OpenCV透明API(TAPI),它基本上利用GPU而不是CPU。几乎所有OpenCV内置算法都支持TAPI,您只需传递cv::UMat而不是cv::Mat。这两种类型可以相互转换。但是,转换是费时的,因为UMat本质上是GPU内存(VRAM)上的一个数组,这意味着每次转换时都必须将其复制。同样,访问VRAM比访问RAM需要更长的时间(对于CPU)。 但是,必须记住,如果不将CPU的VRAM数据复制到RAM中,则无法访问该数据。这意味着如果您使用cv::UMat,则无法迭代像素。仅当您编写自己的OpenCL或Cuda代码以使算法可以在GPU上运行时,才有可能。

在大多数消费级PC中,对于滑动窗口算法(基本上是迭代像素并围绕每个像素执行计算的任何事物),使用GPU通常是迄今为止最快的方法(但也需要花费最大的精力来实现) 。当然,只有在数据缓冲区(您的图像)足够大以至于值得在VRAM之间进行复制时,这种情况才会成立。

对于并行书写:只要没有重叠的区域,通常是安全的。但是,缓存未命中和false sharing(NathanOliver指出)是需要考虑的问题。

答案 1 :(得分:0)

只要在读取中同时发生 次写入,就可以安全地进行多个并发读取。

这适用于任何理智的系统。

考虑备选方案:

如果存在竞争状况,则意味着在读取操作期间存储对象的内存已被修改。如果在读取过程中未写入任何内存(存储对象),则线程之间没有可能的交互。

最后,如果您查看文档,

https://docs.opencv.org/3.1.0/d3/d63/classcv_1_1Mat.html

您将看到两点提到线程安全:

  

因此,在相同的矩阵中异步操作是安全的   不同的线程。

他们提到在矩阵分配期间执行的引用计数。因此,至少可以在多个线程中安全地将同一矩阵分配给另外两个矩阵。这几乎可以保证简单的读取访问也是线程安全的。