【问题标题】:Why Opencv GPU code is slower than CPU?为什么 Opencv GPU 代码比 CPU 慢?
【发布时间】:2012-08-17 22:40:41
【问题描述】:

我在笔记本上使用 opencv242 + VS2010。
我试图在 OpenCV 中对 GPU 块进行一些简单的测试,但它显示 GPU 比 CPU 代码慢 100 倍。 在这段代码中,我只是将彩色图像转为灰度图像,使用 cvtColor

的功能

这是我的代码,PART1 是 CPU 代码(测试 cpu RGB2GRAY),PART2 是上传图像到 GPU,PART3 是 GPU RGB2GRAY,PART4 是 CPU RGB2GRAY。 有三件事让我很想知道:

1 在我的代码中,part1 为 0.3ms,而 part4(与 part1 完全相同)为 40ms!!!
2 上传图片到GPU的part2是6000ms!!!
3 Part3(GPU代码)是11ms,对于这个简单的图像来说太慢了!

    #include "StdAfx.h"
    #include <iostream>
    #include "opencv2/opencv.hpp"
    #include "opencv2/gpu/gpu.hpp"
    #include "opencv2/gpu/gpumat.hpp"
    #include "opencv2/core/core.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include <cuda.h>
    #include <cuda_runtime_api.h>
    #include <ctime>
    #include <windows.h>

    using namespace std;
    using namespace cv;
    using namespace cv::gpu;

    int main()
    {
        LARGE_INTEGER freq;
        LONGLONG QPart1,QPart6;
        double dfMinus, dfFreq, dfTim;
        QueryPerformanceFrequency(&freq);
        dfFreq = (double)freq.QuadPart;

        cout<<getCudaEnabledDeviceCount()<<endl;
        Mat img_src = imread("d:\\CUDA\\train.png", 1);

        // PART1 CPU code~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // From color image to grayscale image.
        QueryPerformanceCounter(&freq);
        QPart1 = freq.QuadPart;
        Mat img_gray;
        cvtColor(img_src,img_gray,CV_BGR2GRAY);
        QueryPerformanceCounter(&freq);
        QPart6 = freq.QuadPart;
        dfMinus = (double)(QPart6 - QPart1);
        dfTim = 1000 * dfMinus / dfFreq;
        printf("CPU RGB2GRAY running time is %.2f ms\n\n",dfTim);

        // PART2 GPU upload image~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        GpuMat gimg_src;
        QueryPerformanceCounter(&freq);
        QPart1 = freq.QuadPart;
        gimg_src.upload(img_src);
        QueryPerformanceCounter(&freq);
        QPart6 = freq.QuadPart;
        dfMinus = (double)(QPart6 - QPart1);
        dfTim = 1000 * dfMinus / dfFreq;
        printf("Read image running time is %.2f ms\n\n",dfTim);

        GpuMat dst1;
        QueryPerformanceCounter(&freq);
        QPart1 = freq.QuadPart;

        /*dst.upload(src_host);*/
        dst1.upload(imread("d:\\CUDA\\train.png", 1));

        QueryPerformanceCounter(&freq);
        QPart6 = freq.QuadPart;
        dfMinus = (double)(QPart6 - QPart1);
        dfTim = 1000 * dfMinus / dfFreq;
        printf("Read image running time 2 is %.2f ms\n\n",dfTim);

        // PART3~ GPU code~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        // gpuimage From color image to grayscale image.
        QueryPerformanceCounter(&freq);
        QPart1 = freq.QuadPart;

        GpuMat gimg_gray;
        gpu::cvtColor(gimg_src,gimg_gray,CV_BGR2GRAY);

        QueryPerformanceCounter(&freq);
        QPart6 = freq.QuadPart;
        dfMinus = (double)(QPart6 - QPart1);
        dfTim = 1000 * dfMinus / dfFreq;
        printf("GPU RGB2GRAY running time is %.2f ms\n\n",dfTim);

        // PART4~CPU code(again)~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        // gpuimage From color image to grayscale image.
        QueryPerformanceCounter(&freq);
        QPart1 = freq.QuadPart;
        Mat img_gray2;
        cvtColor(img_src,img_gray2,CV_BGR2GRAY);
        BOOL i_test=QueryPerformanceCounter(&freq);
        printf("%d \n",i_test);
        QPart6 = freq.QuadPart;
        dfMinus = (double)(QPart6 - QPart1);
        dfTim = 1000 * dfMinus / dfFreq;
        printf("CPU RGB2GRAY running time is %.2f ms\n\n",dfTim);

        cvWaitKey();
        getchar();
        return 0;
    }

【问题讨论】:

  • 并不是说GPU普遍“慢”。但是,主机和设备之间的内存传输非常缓慢。仅当您可以将非常大、高度并行的计算卸载到设备上时,GPU 计算才有意义。
  • 然后你通过未分配的 GpuMat 你在 GPU 优化的函数中有 GPU 内存分配。为避免这种情况,您应该在使用函数之前预先分配适当大小的内存。

标签: c++ opencv gpu


【解决方案1】:

上面的大多数答案实际上都是错误的。它之所以慢 20.000 倍,当然不是因为“CPU 时钟速度更快”和“它必须将其复制到 GPU”(已接受的答案)。这些都是因素,但是说你忽略了一个事实,即你有更多的计算能力来解决一个令人作呕的并行问题。说 20.000 倍的性能差异是因为后者实在是太荒谬了。这里的作者知道有些地方不对劲。解决方案:

你的问题是 CUDA 需要初始化!它总是会初始化第一张图像,通常需要 1-10 秒,具体取决于木星和火星的对齐方式。现在试试这个。计算两次,然后对它们进行计时。在这种情况下,您可能会看到速度在同一数量级内,而不是 20.000 倍,这太荒谬了。你能对这个初始化做点什么吗?不,不是我所知道的。这是一个障碍。

编辑:我刚刚重新阅读了这篇文章。你说你在笔记本上运行。那些通常有破旧的 GPU,而 CPU 的速度还不错。

【讨论】:

  • 这应该是公认的答案,因为它给出了观察到的性能问题的实际原因。 @Tae 已经在 3 年前指出了这一点,但在分析中没有那么明确。
【解决方案2】:

cvtColour 是一个小操作,在 GPU 上执行此操作所获得的任何性能提升都远远超过主机 (CPU) 和设备 (GPU) 之间的内存传输时间。最小化这种内存传输的延迟是任何 GPU 计算的主要挑战。

【讨论】:

    【解决方案3】:

    你有什么 GPU?

    检查计算兼容性,也许是这个原因。

    https://developer.nvidia.com/cuda-gpus

    这意味着对于具有 CC 1.3 和 2.0 二进制图像的设备 准备运行。对于所有较新的平台,1.3 的 PTX 代码是 JIT 的 为二值图像。对于具有 CC 1.1 和 1.2 的设备,1.1 的 PTX 是 JIT'ed。对于具有 CC 1.0 的设备,没有可用的代码,并且 函数抛出异常。适用于 JIT 编译的平台 先执行,运行很慢。

    http://docs.opencv.org/modules/gpu/doc/introduction.html

    【讨论】:

      【解决方案4】:

      尝试多次运行......

      -----------摘自http://opencv.willowgarage.com/wiki/OpenCV%20GPU%20FAQ 性能

      为什么第一个函数调用很慢?

      这是因为初始化开销。在第一个 GPU 函数调用中,Cuda Runtime API 被隐式初始化。在第一次使用时,还会为您的视频卡编译一些 GPU 代码(即时编译)。因此,对于性能测量,有必要进行虚拟函数调用,然后才进行时间测试。

      如果应用程序只运行一次 GPU 代码很重要,则可以使用在多次运行中保持不变的编译缓存。详情请阅读 nvcc 文档(CUDA_DEVCODE_CACHE 环境变量)。

      【讨论】:

        【解决方案5】:

        cvtColor 没有做太多的工作,要使灰色你所要做的就是平均三个数字。

        CPU 上的 cvColor 代码使用 SSE2 指令一次处理多达 8 个像素,如果您有 TBB,它使用所有内核/超线程,CPU 以 GPU 时钟速度的 10 倍运行,最后您不这样做'不必将数据复制到 GPU 上并返回。

        【讨论】:

        • 感谢您的回答!!在这段代码中,我将 CPU 用于 RGB2GRAY 两次(相同的代码,一个在 GPU 代码之前,另一个在 GPU 之后)。它表明第二次比第一次慢得多。但它们是相同的代码!能给我一些提示吗?
        • 这也可能是因为创建 CUDA 上下文需要时间。我不知道您如何评估代码的时序。
        猜你喜欢
        • 2016-08-08
        • 1970-01-01
        • 2022-01-19
        • 2016-10-23
        • 1970-01-01
        • 2020-05-24
        • 2021-02-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多