opencv 多进程的小加速

Posted

技术标签:

【中文标题】opencv 多进程的小加速【英文标题】:Small speedup with multiple processes for opencv 【发布时间】:2017-06-27 11:08:37 【问题描述】:

在应用程序中,我必须分析电影文件 (假设计算连续帧对的差异)。 为此,我使用 opencv(它使用 ffmpeg 作为 lib/编解码器)。 根据视频格式,有不同的 CPU 负载/使用。 对于 wmv3,使用的核心似乎不超过 1 个。 因此,让多个线程处理电影的不同部分是非常接近的, 因为数据是独立的(除了必须在之后缝合部分)。 代码(由 lap 参数剥离)非常简单:

int main(int argc, char *argv[])

    const string source = "move.wmv";

    VideoCapture capt(source);
    if (!capt.isOpened())
    
        cout  << "Could not open file " << source << endl;
        return -1;
    

    unsigned short nThreads (8);

    double *pDiffArray = new double [(size_t) (capt.get(CV_CAP_PROP_FRAME_COUNT)];

    capt.release();

    ComputeDifferences (source, pDiffArray, nThreads);

    return 0;


int ComputeDifferences (const string& source, double *pDiffArray, const unsigned short& nThreads)

    std::vector<std::thread *> threadVector;

    for (unsigned int i=0; i< nThreads; i++)
        threadVector.push_back (new std::thread (ComputePart, source, pDiffArray, nThreads, i));

    for (unsigned int i=0; i< nThreads; i++)
        threadVector.at (i)->join();

    // Stitching
    ;

    return 0;
;


void ComputePart (const string source, double *pDiffArray,
                  const unsigned int& nThreads, const unsigned int& nThreadNo)

    VideoCapture capt(source);
    if (!capt.isOpened())
    
        cout  << "Could not open file " << source << endl;
    

    size_t startPosDiffArray;

    startPosDiffArray = nThreadNo * (capt.get(CV_CAP_PROP_FRAME_COUNT) / nThreads);

    size_t sizePart (capt.get(CV_CAP_PROP_FRAME_COUNT) / nThreads);

    size_t startPosFrame;

    startPosFrame = capt.get(CV_CAP_PROP_FRAME_COUNT) / nThreads * nThreadNo;

    capt.set(CAP_PROP_POS_FRAMES, startPosFrame);

    Size refS = Size((int) capt.get(CAP_PROP_FRAME_WIDTH),
                     (int) capt.get(CAP_PROP_FRAME_HEIGHT));

    Mat frame, frameRes;
    std::array<Mat, 2> frameDuo;
    Scalar s;

    capt >> frameDuo [0];
    if (!frameDuo [0].data)
        return;

    for (size_t i = 1; i < sizePart; i++) 
        capt >> frameDuo [i%2];

        if (!frameDuo [i%2].data)
            break;

        absdiff (frameDuo [(i-1)%2], frameDuo [i%2], frameRes);

        s = sum (frameRes);

        pDiffArray [i-1+startPosDiffArray] = (s [0] + s [1] + s [2])/ (refS.height * refS.width);
    

    capt.release();

如果我在 wmv3 视频上使用它,1280x720,abt。 50,000 帧, 相对于单线程(190 秒),我得到了这个加速(在 Intel i7 上)。

MT2 1.8 MT4 2.6 MT8 3.0

除了非常失望之外,我不明白这里发生了什么。 我确实知道阿姆达尔定律等,但在这种情况下,我预计会有更好的加速。 有没有人对我有提示(作为新手)? 这不是定位(capt.set()),因为禁用不会改变任何东西。 是ffmpeg-lib、opencv、std-lib的线程切换、工作集问题吗?

[编辑:

根据 cmets 中的提示,我发现 80% 的时间用于

capt >> frameDuo [i%2];

这包括从文件中读取、解码和复制到 opencv 结构中。 从这里只有从文件中读取的内容是“顺序类型”的(在 Amdahl 的意义上)。 由于 HDD 没有显示大量访问(即使在 MT8 时),并且没有区别 使用快速 SSD 时,我不明白为什么这个顺序部分会产生如此大的影响。 怎么可能 8 个核心完全工作但只有 3 个加速? 还有:我怎样才能做得更好?]

【问题讨论】:

如果你知道阿姆达尔定律,结果应该不会那么令人惊讶,考虑到你有很多非平行的东西。 可能我理解的不正确。在我看来,没有任何非平行的东西(???)。你能具体点吗?也许然后:如何变得更好...... 总有一个不平行的部分。你打开一个文件,你开始和加入线程等等...... 是的,当然,我明白。但是这些操作(打开文件、启动和加入线程)确实只完成了相当长的(190 秒)工作的一小部分。其中大部分(线程内的处理)应该完全独立完成(???) 你怎么知道它只是很小的一部分?你量过吗? VideoCapture capt(source); 在做什么? 【参考方案1】:

您的大部分数字确实可以用阿姆达尔定律来解释。 如果我将你的结果用于两个线程并尝试计算并行完成的分数,我会得到 p = 0.88888 的值。并将这个值用于 4/8 线程我得到

2   1.8 
4   2.99
8   4.48

这些数字并不能精确地再现您测量的结果,但根据 Amdahls 定律,每个线程都有开销,并且必须考虑更多的东西才能获得真实的数字,所以它只是一个初步近似值,从这个意义上说协议还可以。

作为结论:你得到的数字并没有那么糟糕。当考虑阿姆达尔定律时,这正是人们所期望的大约 85% 的平行分数。

【讨论】:

如何计算并行完成的分数? @KjMag 来自***:S = 1 / (1-p + p/s) 其中 S=1.8 和 s=2 我得到 p = 0.888888

以上是关于opencv 多进程的小加速的主要内容,如果未能解决你的问题,请参考以下文章

多进程可以加速压缩任务多少?

python 复习—并发编程实战——线程多进程多协程加速程序运行实例(多线程和多进程的对比)

Python如何使用多进程加速获取请求

在 Python 多处理进程中运行较慢的 OpenCV 代码片段

多进程——waitpid()函数的小例子

Python QT5 - 多进程 OpenCV 网络摄像头和 Requests.Get