web-dev-qa-db-ja.com

Opencv GPUコードがCPUよりも遅いのはなぜですか?

ノートブックでopencv242 + VS2010を使用しています。
OpenCVでGPUブロックの簡単なテストを試みましたが、GPUがCPUコードよりも100倍遅いことがわかりました。このコードでは、カラー画像をグレースケール画像に変換し、cvtColorの関数を使用します。

これが私のコードです。PART1はCPUコード(test cpu RGB2GRAY)、PART2は画像をGPUにアップロード、PART3はGPU RGB2GRAY、PART4はCPURGB2GRAYです。私がとても不思議に思う3つのことがあります:

1私のコードでは、part1は0.3msですが、part4(part1とまったく同じ)は40msです!!!
2GPUに画像をアップロードする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;
    }
12
David Ding

cvtColorはあまり作業を行っていません。灰色にするために必要なのは、平均3つの数値だけです。

CPUのcvColorコードはSSE2命令を使用して、一度に最大8ピクセルを処理します。また、TBBを使用している場合、すべてのコア/ハイパースレッドを使用している場合、CPUはGPUの10倍のクロック速度で実行され、最終的には必要ありません。データをGPUにコピーして戻します。

25
Martin Beckett

上記のほとんどの答えは実際には間違っています。もちろん、20.000倍遅い理由は、「CPUクロック速度が速い」、「GPUにコピーする必要がある」ためではありません(受け入れられた回答)。これらは要因ですが、嫌なほど並行している問題に対して、はるかに多くの計算能力があるという事実を省略していると言うことによって。 20.000xのパフォーマンスの違いは、後者が非常にばかげているためです。ここの作者は、簡単ではない何かが間違っていることを知っていました。解決:

あなたの問題はCUDAが初期化する必要があるということです!それは常に最初の画像のために初期化され、木星と火星の配置に応じて、通常1-10秒かかります。今これを試してみてください。計算を実行しますtwiceそして両方の時間を計ります。この場合、速度が20.000xではなく、マグナチドと同じオーダー内にあることがわかるでしょう。これはばかげています。この初期化について何かできますか?いいえ、私が知っていることではありません。それは障害です。

編集:私はただ投稿を読み直しました。あなたはあなたがノートブックで走っていると言います。それらはしばしばぼろぼろのGPUと公正なターボを備えたCPUを持っています。

22
TimZaman

複数回実行してみてください...

-----------抜粋 http://opencv.willowgarage.com/wiki/OpenCV%20GPU%20FAQ パフォーマンス

最初の関数呼び出しが遅いのはなぜですか?

これは、初期化のオーバーヘッドが原因です。最初のGPU関数呼び出しで、Cuda RuntimeAPIは暗黙的に初期化されます。また、一部のGPUコードは、最初の使用時にビデオカード用にコンパイルされます(ジャストインタイムコンパイル)。したがって、パフォーマンス測定のために、ダミーの関数呼び出しを実行してから、時間テストを実行する必要があります。

アプリケーションがGPUコードを1回だけ実行することが重要な場合は、複数の実行にわたって永続的なコンパイルキャッシュを使用できます。詳細については、nvccのドキュメントをお読みください(CUDA_DEVCODE_CACHE環境変数)。

6
Tae

cvtColourは小さな操作であり、GPUで実行することで得られるパフォーマンスの向上は、ホスト(CPU)とデバイス(GPU)間のメモリ転送時間よりもはるかに重要です。このメモリ転送の遅延を最小限に抑えることは、GPUコンピューティングの主要な課題です。

1
1''

どのGPUがありますか?

計算の互換性を確認してください。おそらくそれが理由です。

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

これは、CC1.3および2.0のバイナリイメージを備えたデバイスで実行する準備ができていることを意味します。すべての新しいプラットフォームでは、1.3のPTXコードがバイナリイメージにJITされます。 CC 1.1および1.2を搭載したデバイスの場合、1.1のPTXはJITされます。 CC 1.0を搭載したデバイスの場合、使用可能なコードはなく、関数は例外をスローします。 JITコンパイルが最初に実行されるプラットフォームの場合、実行は遅くなります。

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

0
mrgloom