OPENCV :不同方法的 CUDA 上下文初始化

OPENCV : CUDA context initialization for different methods

本文关键字:CUDA 上下文 初始化 方法 OPENCV      更新时间:2023-10-16

我正在开发一个简单的 c++ 程序来评估一些 Opencv GPU 方法的性能 (cv::cuda)。我在 Ubuntu 15(带有 CUDA 7.5)和 GeForce 770 上使用 Opencv 3.1。

我之前读过我们需要初始化 CUDA 环境以避免第一次调用时进程缓慢。因此,我使用 cv::cuda::getDevice() 和 setDevice() 初始化我的程序。

然后,我测试2种方法:

  • cv::cuda::resize() (系数 0.5)
  • 和 cv::cuda::meanStdDev。

初始化需要 400 毫秒。然后,调整大小需要 2 或 3 毫秒,没关系。但。。。平均标准开发需要 476 毫秒!如果我连续运行 2 个 meanStdDev,第二个要快得多(3 毫秒)。

我真的不明白为什么初始化对 resize() 有影响,但对 meanStdDev() 没有影响。

我用 -DCUDA_ARCH_BIN=3.0 编译 OPENCV。我尝试使用 -DCUDA_ARCH_PTX=",但问题仍然相同。

#include <opencv2/opencv.hpp>
#include <opencv2/cudaimgproc.hpp>
#include "opencv2/cudawarping.hpp"
#include "opencv2/cudaarithm.hpp"
using namespace std;
int main(int argc, char *argv[]) 
{
    double t_init_cuda = (double)cv::getTickCount();
    int CudaDevice;
    if(cv::cuda::getCudaEnabledDeviceCount()==0)
    {
        cerr<<endl<<"ERROR: NO CudaEnabledDevice"<<endl;
        exit(2);
    }
    else
    {
        CudaDevice = cv::cuda::getDevice();
        cv::cuda::setDevice(CudaDevice);
    }
    t_init_cuda = ((double)cv::getTickCount() - t_init_cuda)/cv::getTickFrequency() * 1000;
    cout<<endl<<"t*T_INIT_CUDA="<<t_init_cuda<<"msn";;
    cv::Mat src = cv::imread(argv[1], 0);
    if (!src.data) exit(1);
    cv::cuda::GpuMat d_src(src);

    //CV::CUDA::RESIZE
    cv::cuda::GpuMat d_dst;
    double factor = 0.5;
    double t_gpu_resize = cv::getTickCount();
    cv::cuda::resize(d_src, d_dst, cv::Size( (int) ((float) (d_src.cols)*factor) , (int) ((float) (d_src.rows)*factor)), 0, 0, CV_INTER_AREA);
    t_gpu_resize = ((double)cv::getTickCount() - t_gpu_resize)/cv::getTickFrequency() * 1000;
    cout<<endl<<"D_SRC="<<d_src.rows<<"x"<<d_src.cols<<" => D_DST="<<d_dst.rows<<"x"<<d_dst.cols<<endl;
    cout<<endl<<"t*T_GPU_RESIZE="<<t_gpu_resize<<"msn";;
    //CV::CUDA::MEANSTDDEV
    double t_meanstddev = (double)cv::getTickCount();
    cv::Scalar mean, stddev;
    std::vector<cv::cuda::GpuMat> d_src_split;
    cv::cuda::split(d_src, d_src_split);
    cv::cuda::meanStdDev (d_src_split[0], mean, stddev); 
    t_meanstddev = ((double)cv::getTickCount() - t_meanstddev)/cv::getTickFrequency() * 1000.0;
    cout<<endl<<"mean="<<mean.val[0]<<" | stddev="<<stddev.val[0]<<endl;    
    cout<<endl<<"t*T_GPU_MEANSTDDEV="<<t_meanstddev<<"msn";
    return 0;
}

我的朋友,当你调用同一个函数两次时:

1-首次在设备上分配新内存以调整大小。"根据OpenCV的WIKI"

2-第二次重复使用分配的内存,因此速度很快。

我从OpenCV中为你获取了这个功能,所以你可以理解它为什么这么说。

void cv::cuda::meanStdDev(InputArray _src, OutputArray _dst, Stream& stream)
{
    if (!deviceSupports(FEATURE_SET_COMPUTE_13))
        CV_Error(cv::Error::StsNotImplemented, "Not sufficient compute capebility");
    const GpuMat src = getInputMat(_src, stream);
    CV_Assert( src.type() == CV_8UC1 );
    GpuMat dst = getOutputMat(_dst, 1, 2, CV_64FC1, stream);
    NppiSize sz;
    sz.width  = src.cols;
    sz.height = src.rows;
    int bufSize;
#if (CUDA_VERSION <= 4020)
    nppSafeCall( nppiMeanStdDev8uC1RGetBufferHostSize(sz, &bufSize) );
#else
    nppSafeCall( nppiMeanStdDevGetBufferHostSize_8u_C1R(sz, &bufSize) );
#endif
    BufferPool pool(stream);
    GpuMat buf = pool.getBuffer(1, bufSize, CV_8UC1); // <--- this line create new GpuMat
    NppStreamHandler h(StreamAccessor::getStream(stream));
    nppSafeCall( nppiMean_StdDev_8u_C1R(src.ptr<Npp8u>(), static_cast<int>(src.step), sz, buf.ptr<Npp8u>(), dst.ptr<Npp64f>(), dst.ptr<Npp64f>() + 1) );
    syncOutput(dst, _dst, stream);
}

此函数

GpuMat cv::cuda::BufferPool::getBuffer(int rows, int cols, int type)
{
    GpuMat buf(allocator_);
    buf.create(rows, cols, type);
    return buf;
}

我希望这对你有帮助。