【问题标题】:GPU versions of OpenCV algorithms slower than CPU versions on my machine?OpenCV 算法的 GPU 版本比我机器上的 CPU 版本慢?
【发布时间】:2013-01-11 03:23:15
【问题描述】:

在尝试使用带有 OpenCV 的 GPU 加速简单算法时,我注意到在我的机器(Ubuntu 12.10、NVidia 9800GT、Cuda 4.2.9、g++ 4.7.2)上,GPU 版本实际上比 CPU 版本慢.我用下面的代码进行了测试。

#include <opencv2/opencv.hpp>
#include <opencv2/gpu/gpu.hpp>

#include <chrono>
#include <iostream>

int main()
{
    using namespace cv;
    using namespace std;

    Mat img1(512, 512, CV_32FC3, Scalar(0.1f, 0.2f, 0.3f));
    Mat img2(128, 128, CV_32FC3, Scalar(0.2f, 0.3f, 0.4f));
    Mat img3(128, 128, CV_32FC3, Scalar(0.3f, 0.4f, 0.5f));

    auto startCPU = chrono::high_resolution_clock::now();
    double resultCPU(0.0);
    cout << "CPU ... " << flush;
    for (int y(0); y < img2.rows; ++y)
    {
        for (int x(0); x < img2.cols; ++x)
        {
            Mat roi(img1(Rect(x, y, img2.cols, img2.rows)));
            Mat diff;
            absdiff(roi, img2, diff);
            Mat diffMult(diff.mul(img3));
            Scalar diffSum(sum(diff));
            double diffVal(diffSum[0] + diffSum[1] + diffSum[2]);
            resultCPU += diffVal;
        }
    }
    auto endCPU = chrono::high_resolution_clock::now();
    auto elapsedCPU = endCPU - startCPU;
    cout << "done. " << resultCPU << " - ticks: " << elapsedCPU.count() << endl;

    gpu::GpuMat img1GPU(img1);
    gpu::GpuMat img2GPU(img2);
    gpu::GpuMat img3GPU(img3);
    gpu::GpuMat diffGPU;
    gpu::GpuMat diffMultGPU;
    gpu::GpuMat sumBuf;

    double resultGPU(0.0);
    auto startGPU = chrono::high_resolution_clock::now();
    cout << "GPU ... " << flush;
    for (int y(0); y < img2GPU.rows; ++y)
    {
        for (int x(0); x < img2GPU.cols; ++x)
        {
            gpu::GpuMat roiGPU(img1GPU, Rect(x, y, img2GPU.cols, img2GPU.rows));
            gpu::absdiff(roiGPU, img2GPU, diffGPU);
            gpu::multiply(diffGPU, img3GPU, diffMultGPU);
            Scalar diffSum(gpu::sum(diffMultGPU, sumBuf));
            double diffVal(diffSum[0] + diffSum[1] + diffSum[2]);
            resultGPU += diffVal;
        }
    }
    auto endGPU = chrono::high_resolution_clock::now();
    auto elapsedGPU = endGPU - startGPU;
    cout << "done. " << resultGPU << " - ticks: " << elapsedGPU.count() << endl;
}

我的结果如下:

CPU ... done. 8.05306e+07 - ticks: 4028470
GPU ... done. 3.22122e+07 - ticks: 5459935

如果这有帮助:我的分析器(System Profiler 1.1.8)告诉我大部分时间都花在cudaDeviceSynchronize

我在使用 OpenCV GPU 函数的方式上做错了什么,还是我的 GPU 太慢了?

【问题讨论】:

  • 如果你调用 Scalar diffSum(gpu::sum(diffMultGPU, sumBuf));,我不完全确定 opencv 的行为方式,我认为它只会从标量元素生成 memcpy。因此,对于每个图像元素,您都需要从 gpu 到主机进行微小的复制操作。那会花费很多时间。
  • 也许您可以编写一个迷你内核来代替这一行,它将 diffvalresultGPU 保留在 gpu 上,而两个 for 循环仅在最后您将添加一个memcpy 从 cuda 到主机的最终结果。
  • 在嵌套 for 循环之前为 MatGpuMat 对象分配内存空间可能会提高 CPU 和 GPU 性能。
  • GPU 是如何变慢的? 3.22 小于 8.05
  • @KansaiRobot 3.22 和 8.05 不是持续时间,它们是校验和。 GPU 速度较慢,因为它需要 5459935(时间)滴答,但 CPU 只需要 4028470 滴答。

标签: c++ opencv cuda gpu


【解决方案1】:

感谢集线器的 cmets 和 Eric,我能够以 GPU 版本实际上比 CPU 版本更快的方式更改我的测试。现在也消除了导致两个版本的不同校验和的错误。 ;-)

#include <opencv2/opencv.hpp>
#include <opencv2/gpu/gpu.hpp>

#include <chrono>
#include <iostream>

int main()
{
    using namespace cv;
    using namespace std;

    Mat img1(512, 512, CV_32FC3, Scalar(1.0f, 2.0f, 3.0f));
    Mat img2(128, 128, CV_32FC3, Scalar(4.0f, 5.0f, 6.0f));
    Mat img3(128, 128, CV_32FC3, Scalar(7.0f, 8.0f, 9.0f));
    Mat resultCPU(img2.rows, img2.cols, CV_32FC3, Scalar(0.0f, 0.0f, 0.0f));

    auto startCPU = chrono::high_resolution_clock::now();
    cout << "CPU ... " << flush;
    for (int y(0); y < img1.rows - img2.rows; ++y)
    {
        for (int x(0); x < img1.cols - img2.cols; ++x)
        {
            Mat roi(img1(Rect(x, y, img2.cols, img2.rows)));
            Mat diff;
            absdiff(roi, img2, diff);
            Mat diffMult(diff.mul(img3));
            resultCPU += diffMult;
        }
    }
    auto endCPU = chrono::high_resolution_clock::now();
    auto elapsedCPU = endCPU - startCPU;
    Scalar meanCPU(mean(resultCPU));
    cout << "done. " << meanCPU << " - ticks: " << elapsedCPU.count() << endl;

    gpu::GpuMat img1GPU(img1);
    gpu::GpuMat img2GPU(img2);
    gpu::GpuMat img3GPU(img3);
    gpu::GpuMat diffGPU(img2.rows, img2.cols, CV_32FC3);
    gpu::GpuMat diffMultGPU(img2.rows, img2.cols, CV_32FC3);
    gpu::GpuMat resultGPU(img2.rows, img2.cols, CV_32FC3, Scalar(0.0f, 0.0f, 0.0f));

    auto startGPU = chrono::high_resolution_clock::now();
    cout << "GPU ... " << flush;
    for (int y(0); y < img1GPU.rows - img2GPU.rows; ++y)
    {
        for (int x(0); x < img1GPU.cols - img2GPU.cols; ++x)
        {
            gpu::GpuMat roiGPU(img1GPU, Rect(x, y, img2GPU.cols, img2GPU.rows));
            gpu::absdiff(roiGPU, img2GPU, diffGPU);
            gpu::multiply(diffGPU, img3GPU, diffMultGPU);
            gpu::add(resultGPU, diffMultGPU, resultGPU);
        }
    }
    auto endGPU = chrono::high_resolution_clock::now();
    auto elapsedGPU = endGPU - startGPU;
    Mat downloadedResultGPU(resultGPU);
    Scalar meanGPU(mean(downloadedResultGPU));
    cout << "done. " << meanGPU << " - ticks: " << elapsedGPU.count() << endl;
}

输出:

CPU ... done. [3.09658e+06, 3.53894e+06, 3.98131e+06, 0] - ticks: 34021332
GPU ... done. [3.09658e+06, 3.53894e+06, 3.98131e+06, 0] - ticks: 20609880

这不是我预期的加速,但可能我的 GPU 不是最适合这些东西的。谢谢各位。

【讨论】:

  • 在比较性能时,我不想排除 CPU-GPU 数据传输延迟。在决定是否使用 GPU 时,传输时间是一个重要的实际考虑因素,无论数据在卡上后算法是否性能更好。通常,在 CPU 上处理小矩阵的速度更快,而 GPU 只有在超出给定大小的情况下才能获得回报,此时延迟会被性能优势所掩盖。
  • 我不明白您为什么改变了评估绩效的方法以及改变了什么。垫子?? (结果CPU)
  • @KansaiRobot resultCPUresultGPU 不是衡量性能/持续时间的值。这些是校验和,仅用于确保两种方法(CPU 和 GPU)给出相同的结果。持续时间测量值位于 elapsedCPUelapsedGPU 中,并以“ticks”的形式打印到标准输出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-14
  • 2018-02-01
  • 2017-04-05
  • 2018-07-08
  • 2017-10-14
  • 1970-01-01
  • 2021-01-27
相关资源
最近更新 更多