为什么Opencv GPU代码比CPU慢

Why Opencv GPU code is slower than CPU?

本文关键字:CPU 代码 Opencv GPU 为什么      更新时间:2023-10-16

我正在笔记本上使用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的第二部分是6000ms
3第3部分(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 msnn",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 msnn",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 msnn",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 msnn",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 msnn",dfTim);
        cvWaitKey();
        getchar();
        return 0;
    }

上面的大多数答案实际上都是错误的。它慢20.000倍的原因当然不是因为"CPU时钟速度更快"answers"它必须将其复制到GPU"(公认的答案)。这些都是因素,但如果说你忽略了一个事实,即你对一个令人厌恶的平行问题有更大的计算能力。说20000倍的性能差异是因为后者太荒谬了。这里的作者知道有些不对劲,这不是直截了当的。解决方案:

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

编辑:我刚刚重读了这篇文章。你说你在笔记本上跑步。这些通常都有破旧的GPU,CPU有一个公平的涡轮。

cvtColor并没有做太多的工作,要使灰色化,只需要平均三个数字。

CPU上的cvColor代码使用SSE2指令一次处理多达8个像素,如果你有TBB,它使用所有核心/超线程,CPU的运行速度是GPU的10倍,最后你不必将数据复制到GPU上再复制回来。

尝试运行多次。。。。

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

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

这是因为初始化开销。在第一个GPU函数调用上,Cuda Runtime API被隐式初始化。此外,一些GPU代码是为您的视频卡在第一次使用时编译的(实时编译)。因此,为了衡量性能,有必要进行伪函数调用,然后再进行时间测试。

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

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

你有什么GPU?

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

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

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

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