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

Is it thread-safe to access a Mat with multiple threads in OpenCV?

本文关键字:线程 Mat 是否 安全 OpenCV 访问      更新时间:2023-10-16

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

目前,我通过使用一个线程/进程遍历所有像素来做到这一点。我想通过将输入图像划分为多个ROI并分别计算每个ROI(使用多个线程(来并行执行此任务。

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

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

只要没有同时对读取进行写入,就可以安全地进行多个并发读取。

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

考虑替代方案:

如果存在争用条件,则意味着存储对象的内存在读取操作期间被修改。如果在读取过程中没有写入内存(存储对象(,则线程之间不可能进行交互。

最后,如果你看一下文档,

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

您将看到两次提及线程安全:

因此,在 不同的线程。

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

一般来说,并行读取不是问题,因为cv::Mat只是数组的一个很好的包装器,就像std::vector一样(是的,有差异,但我看不出它们如何影响这里的主题问题,所以我将忽略它们(。但是,并行化不会自动提升性能。这里有很多事情需要考虑:

创建线程的资源量很大,如果任务相对较短(就计算时间而言(,则可能会产生很大的负面影响,因此必须考虑线程池。

如果您编写高性能代码(无论是多线程还是单线程(,您应该掌握硬件的工作原理。在这种情况下:内存和CPU。Timur Doumler在CppCon 2016上就这个话题做了很好的演讲。这应该可以帮助您避免缓存未命中。

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

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

在大多数消费级PC中,对于滑动窗口算法(基本上是迭代像素并围绕每个像素执行计算的任何内容(,使用GPU通常是迄今为止最快的方法(但也需要最多的努力来实现(。当然,这仅在数据缓冲区(您的图像(足够大以使其值得复制到 VRAM 和从 VRAM 复制时才成立。

对于并行写作:只要您没有重叠区域,它通常是安全的。但是,缓存未命中和错误共享(如NathanOliver指出的那样(是需要考虑的问题。