我想加速一种算法(具有圆邻的完整局部二进制模式),为此我遍历所有像素并计算与它相邻的东西(因此我需要相邻像素访问)。
目前,我是通过一个线程/进程遍历所有像素来实现的。我想通过将输入图像分为多个ROI并分别计算每个ROI(使用多个线程)来并行化此任务。
这里的问题是,ROI重叠(因为要计算像素,有时我需要查看远处的邻居),并且可能有多个线程访问Pixel-Data( READING )在同一时间。如果两个或多个线程同时读取相同索引的Mat,这是否有问题?
如果我以相同的并行度但以不同的索引写入同一Mat,这也是一个问题吗?
答案 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
您将看到两点提到线程安全:
因此,在相同的矩阵中异步操作是安全的 不同的线程。
他们提到在矩阵分配期间执行的引用计数。因此,至少可以在多个线程中安全地将同一矩阵分配给另外两个矩阵。这几乎可以保证简单的读取访问也是线程安全的。