动态图像大小调整的GPU与CPU端到端延迟

时间:2018-02-22 07:22:52

标签: opencv imagemagick gpu cpu throughput

我目前使用OpenCV和ImageMagick进行一些吞吐量基准测试,我发现使用GPU比CPU更快。我们在网站上的用例是根据服务调用动态调整主副本请求的大小,并尝试评估GPU是否有意义动态调整每个服务调用的大小。

分享我为OpenCV编写的代码。我正在为串行存储在文件夹中的所有图像运行以下函数。最终我正在运行N个这样的过程来实现X个图像调整大小。我想了解我的方法是否不正确评估或者如果用例没有不适合典型的GPU用例。什么可能限制GPU性能。我甚至没有将利用率最大化到接近100%的任何地方

resizeGPU.cpp : {

    cv::Mat::setDefaultAllocator(cv::cuda::HostMem::getAllocator (cv::cuda::HostMem::AllocType::PAGE_LOCKED));

    auto t_start = std::chrono::high_resolution_clock::now();
    Mat src = imread(input_file,CV_LOAD_IMAGE_COLOR);
    auto t_end_read = std::chrono::high_resolution_clock::now();
    if(!src.data){
            std::cout<<"Image Not Found: "<< input_file << std::endl;
            return;
    }

    cuda::GpuMat d_src;
    d_src.upload(src,stream);
    auto t_end_h2d = std::chrono::high_resolution_clock::now();
    cuda::GpuMat d_dst;

    cuda::resize(d_src, d_dst, Size(400, 400),0,0, CV_INTER_AREA,stream);
    auto t_end_resize = std::chrono::high_resolution_clock::now();

    Mat dst;
    d_dst.download(dst,stream);
    auto t_end_d2h = std::chrono::high_resolution_clock::now();
    std::cout<<"read,"<<std::chrono::duration<double, std::milli>(t_end_read-t_start).count()<<",host2device,"<<std::chrono::duration<double, std::milli>(t_end_h2d-t_end_read).count()
                            <<",resize,"<<std::chrono::duration<double, std::milli>(t_end_resize-t_end_h2d).count()
                            <<",device2host,"<<std::chrono::duration<double, std::milli>(t_end_d2h-t_end_resize).count()
            <<",total,"<<std::chrono::duration<double, std::milli>(t_end_d2h-t_start).count()<<endl;


}

resizeCPU.cpp:

    auto t_start = std::chrono::high_resolution_clock::now();
    Mat src = imread(input_file,CV_LOAD_IMAGE_COLOR);
    auto t_end_read = std::chrono::high_resolution_clock::now();
    if(!src.data){
            std::cout<<"Image Not Found: "<< input_file << std::endl;
            return;
    }

    Mat dst;
    resize(src, dst, Size(400, 400),0,0, CV_INTER_AREA);
    auto t_end_resize = std::chrono::high_resolution_clock::now();

    std::cout<<"read,"<<std::chrono::duration<double, std::milli>(t_end_read-t_start).count()<<",resize,"<<std::chrono::duration<double, std::milli>(t_end_resize-t_end_read).count()
        <<",total,"<<std::chrono::duration<double, std::milli>(t_end_resize-t_start).count()<<endl;

正在编译:g ++ -std = c ++ 11 resizeCPU.cpp -o resizeCPU pkg-config --cflags --libs opencv

我运行每个程序N次,由以下代码控制:runMultipleGPU.sh

#!/bin/bash
echo $1
START=1
END=$1
for (( c=$START; c<=$END; c++ ))
do
./resizeGPU "$c" &#>/dev/null #&disown;
done
wait
echo All done

运行:./ runMultipleGPU.sh

这些定时器导致跟随汇总数据

No_processes    resizeCPU   resizeGPU   memcpyGPU   totalresizeGPU
1                 1.51        0.55        2.13         2.68
10                5.67        0.37        2.43         2.80
15                6.35        2.30       12.45        14.75
20                6.30        2.05       10.56        12.61
30                8.09        4.57       23.97        28.55

每个进程都没有运行图像:267

图片的平均尺寸:624Kb

根据上面的数据,随着我们增加进程数量(导致同时调整大小的数量增加),调整大小执行 在GPU与CPU之间,ance(包括实际调整大小+主机到设备和设备到主机副本)显着增加。

使用在

下使用OpenCL的ImageMagick后的类似结果

代码

setenv("MAGICK_OCL_DEVICE","OFF",1); //Turn in ON to use GPU acceleration
Image image;
auto t_start_read = std::chrono::high_resolution_clock::now();
image.read( full_path );
auto t_end_read = std::chrono::high_resolution_clock::now();
image.resize( Geometry(400,400) );
auto t_end_resize = std::chrono::high_resolution_clock::now();

结果

No_procs    resizeCPU   resizeGPU
1            63.23       8.54
10           76.16      31.04
15           76.56      50.79
20           76.58      71.68
30           86.29     140.17

测试机器配置:

4 GPU(Tesla P100) - 但测试仅使用1个GPU

64个CPU内核(通过Intel Xeon 2680 v4 CPU)

OpenCV版本:3.4.0

ImageMagick版本:6.9.9-26 Q16 x86_64 2018-01-17

Cuda Toolkit:9.0

1 个答案:

答案 0 :(得分:0)

高度可行,为时已晚,无法为您提供帮助。但是,对于希望看到此答案的人们,这是我建议提高性能的建议。设置固定内存的方式并不能帮助您寻找所需的内存。 这是:使用

//method 1
cv::Mat::setDefaultAllocator(cv::cuda::HostMem::getAllocator(cv::cuda::HostMem::AllocType::PAGE_LOCKED));

this discussion的注释中。有人建议像你一样做。回答者说,速度较慢。我正在安排实施与Coldvision.io Sobel中的sobel衍生品相近的sobel衍生品,主要步骤是:

  1. 读取彩色图像
  2. 彩色图像的
  3. 高斯模糊 半径为3,增量为1;
  4. 灰度转换;
  5. 计算x和y梯度
  6. 将它们合并到最终输出图像中。

相反,我实现了一个版本,交换了第2步和第3步的顺序。先转换为灰度,然后通过传递高斯对结果进行降噪。

我在Windows 10中运行openCV3.4。CUDA9.0。我的CPU是i7-6820HQ。 GPU是Quadro M1200。

我尝试您的方法以及这一方法:

//Method 2
//allocate pinned memory    
cv::cuda::HostMem memory(siz.height, siz.width, CV_8U, cv::cuda::HostMem::PAGE_LOCKED);
//Read input image from the disk
Mat input = imread(input_file, CV_LOAD_IMAGE_COLOR);
if (input.empty())
{
    std::cout << "Image Not Found: " << input_file << std::endl;
    return;
}    
input.copyTo(memory);
// copy the input image from CPU to GPU memory
cuda::GpuMat gpuInput;
cv::cuda::Stream stream;
gpuInput.upload(memory, stream);

//Do your processing... 

//allocate pinned memory for output
cv::cuda::HostMem outMemory(siz.height, siz.width, CV_8U, cv::cuda::HostMem::PAGE_LOCKED);
gpuOutput.download(outMemory, stream);
cv::Mat output = outMemory.createMatHeader();

我将增益计算为:(t1-t2)/t1*100。其中t1是正常运行代码的时间。 t2使用固定内存运行它。负值是当该方法比在非固定内存中运行慢时。

image size  Gain % Method 1    Gain % Method 2
800x600     2.9                8.2
1280x1024   2.5                15.3
1600x1200   0.2                7.0
2048x1536   -2.3               14.6
4096x3072   -1.0               17.2