目录
测试环境
显卡:1050TI,
CPU:I9-13900
CUDA版本11.4
opencv版本4.8.0
测试代码1
每次只进行一次运算,记录时间不包括读取图片和上传GPU的过程
//cpu code
cv::Mat verticalProjection;
cv::Mat horizontalProjection;
cv::Mat diffImage, diffImage2;
cv::Mat testImage = imread("test.png", 0);
cv::Mat backImage = imread("background.png", 0);
cv::Mat backImage_diff = imread("background_diff.png", 0);
auto startTime_1 = std::chrono::high_resolution_clock::now();
cv::absdiff(backImage, testImage, diffImage);
cv::absdiff(backImage_diff, diffImage, diffImage2);
cv::reduce(diffImage2, verticalProjection, 0, REDUCE_SUM, CV_32S);
cv::reduce(diffImage2, horizontalProjection, 1, REDUCE_SUM, CV_32S);
auto endTime_1 = std::chrono::high_resolution_clock::now();
Calculate the duration
auto duration_1 = std::chrono::duration_cast<std::chrono::microseconds>(endTime_1 - startTime_1).count();
std::cout << "CPU 处理耗时 平均1000次: " << duration_1 / 1000 << " 微秒." << std::endl;
CUDA部分
//cuda code
cv::Mat gpu_verticalProjection;
cv::Mat gpu_horizontalProjection;
cv::Mat gpu_diffImage, gpu_diffImage2;
cv::Mat gpu_testImage = imread("test.png", 0);
cv::Mat gpu_backImage = imread("background.png", 0);
cv::Mat gpu_backImage_diff = imread("background_diff.png", 0);
//cuda code
cv::cuda::GpuMat cuda_Image,cuda_backImage_1, cuda_backImage_2,cuda_diff_1, cuda_diff_2;
cv::cuda::GpuMat cuda_horizontalProjection, cuda_verticalProjection;
cuda_Image.upload(gpu_testImage);
cuda_backImage_1.upload(gpu_backImage);
cuda_backImage_2.upload(gpu_backImage_diff);
auto startTime = std::chrono::high_resolution_clock::now();
cv::cuda::absdiff(cuda_Image, cuda_backImage_1, cuda_diff_1);
cv::cuda::absdiff(cuda_diff_1, cuda_backImage_2, cuda_diff_2);
cv::cuda::reduce(cuda_diff_2, cuda_verticalProjection, 0, cv::REDUCE_SUM, CV_32S);
cv::cuda::reduce(cuda_diff_2, cuda_verticalProjection, 1, cv::REDUCE_SUM, CV_32S);
auto endTime = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime).count();
std::cout << "GPU 处理耗时 平均1000次: " << duration/1000 << " 微秒." << std::endl;
测试结果
可以发现GPU速度上严重慢于GPU
测试代码2
加入循环1000次,求平均时间
//cpu code
cv::Mat verticalProjection;
cv::Mat horizontalProjection;
cv::Mat diffImage, diffImage2;
cv::Mat testImage = imread("test.png", 0);
cv::Mat backImage = imread("background.png", 0);
cv::Mat backImage_diff = imread("background_diff.png", 0);
auto startTime_1 = std::chrono::high_resolution_clock::now();
while ((num++) < 1000)
{
cv::absdiff(backImage, testImage, diffImage);
cv::absdiff(backImage_diff, diffImage, diffImage2);
cv::reduce(diffImage2, verticalProjection, 0, REDUCE_SUM, CV_32S);
cv::reduce(diffImage2, horizontalProjection, 1, REDUCE_SUM, CV_32S);
}
auto endTime_1 = std::chrono::high_resolution_clock::now();
Calculate the duration
auto duration_1 = std::chrono::duration_cast<std::chrono::microseconds>(endTime_1 - startTime_1).count();
std::cout << "CPU 处理耗时 平均1000次: " << duration_1 / 1000 << " 微秒." << std::endl;
cuda代码
//cuda code
cv::Mat gpu_verticalProjection;
cv::Mat gpu_horizontalProjection;
cv::Mat gpu_diffImage, gpu_diffImage2;
cv::Mat gpu_testImage = imread("test.png", 0);
cv::Mat gpu_backImage = imread("background.png", 0);
cv::Mat gpu_backImage_diff = imread("background_diff.png", 0);
//cuda code
cv::cuda::GpuMat cuda_Image,cuda_backImage_1, cuda_backImage_2,cuda_diff_1, cuda_diff_2;
cv::cuda::GpuMat cuda_horizontalProjection, cuda_verticalProjection;
cuda_Image.upload(gpu_testImage);
cuda_backImage_1.upload(gpu_backImage);
cuda_backImage_2.upload(gpu_backImage_diff);
auto startTime = std::chrono::high_resolution_clock::now();
while ((num++)<1000)
{
cv::cuda::absdiff(cuda_Image, cuda_backImage_1, cuda_diff_1);
cv::cuda::absdiff(cuda_diff_1, cuda_backImage_2, cuda_diff_2);
cv::cuda::reduce(cuda_diff_2, cuda_verticalProjection, 0, cv::REDUCE_SUM, CV_32S);
cv::cuda::reduce(cuda_diff_2, cuda_verticalProjection, 1, cv::REDUCE_SUM, CV_32S);
}
auto endTime = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(endTime - startTime).count();
std::cout << "GPU 处理耗时 平均1000次: " << duration/1000 << " 微秒." << std::endl;
测试结果
可以发现GPU处理速度提升明显,已经能和CPU达到一个数量级了
可以看到,GPU并没有多大占用率
总结
首先问问神奇的GPT
问:
为什么会出现使用cv::cuda::absdiff的处理函数,运算速度还比不过CPU版本的cv::absdiff?
答:
使用cv::cuda::absdiff
处理函数时,CUDA加速的性能优势并不是绝对的,它取决于很多因素。以下是一些可能导致CUDA版本运算速度较慢的原因:
- 数据传输开销:CUDA涉及将数据从主机(CPU)传输到GPU,以及从GPU传输回主机。如果处理的图像大小较小,数据传输的开销可能会导致CUDA版本的运算速度比CPU版本慢。
- 启动开销:在CUDA版本中,启动GPU核函数涉及GPU上下文的切换和其他开销,这可能在处理小图像时成为瓶颈。
- 硬件限制:如果GPU的计算性能相对较低,或者GPU内存不足以容纳大的图像,那么CUDA加速可能并不会带来明显的性能提升。
- 算法复杂性:有些算法在GPU上的并行处理效果不如CPU上的串行处理。CUDA适合处理那些可以被有效地并行化的算法。
- 代码优化:CUDA代码需要特定的优化技巧和策略,包括合理使用共享内存、避免线程间冲突等。如果CUDA代码没有经过充分优化,其性能可能不如预期。
- 并行度:CUDA加速通常在处理大规模图像或大规模数据集时效果更为明显。对于小规模问题,GPU并行处理的优势可能较小。
如果发现CUDA版本的运算速度比CPU版本慢,可以考虑以下优化措施:
- 增加输入图像的大小,以使得CUDA加速的优势更为明显。
- 确保在GPU上的数据传输和计算开销不会成为性能瓶颈,可以一次性传输多个图像进行并行处理。
- 优化CUDA核函数的实现,避免线程冲突,并利用共享内存等技术提高计算效率。
- 调整GPU的计算性能、内存等资源配置,以适合实际处理的任务。
最终,CUDA加速的性能取决于具体问题的特征和硬件环境,因此需要进行实际测试和优化,以找到最佳的实现方式。
个人总结
尽管是像上面那么简单的4个语句,三个图片的上传,只要是整段代码的第一次调用gpu相关操作,默认执行CUDA的初始化。upload这个过程就会影响到后面的cv::cuda函数执行速度,在其它机器上不一定是我这个测试时间,应该跟具体的PC硬件有关,看看CPU能初始化cuda有多快。
往后的gpu的操作都不会那么慢,但一旦涉及upload、download(显存与内存的数据传递),时间成本都达到ms级别!GPU的并行处理的确很快,但数据传入GPU的开销实在太大,往往影响了代码的整体效率。
优化思路
目前看,需要将图片或者视频大批量传入GPU后进行批量运算才能最大幅度提高cuda处理效率,100帧或者1000帧处理一次性完成,单帧处理速度是不能看的。
需要学会并行处理的思路,这里可以参考一些FPGA上关于图像处理的设计:
图像数据串行进入后转成多个通道并行处理,并行处理完成后再转成串行回到CPU,每个并行通道中的运算过程中,还要考虑计算流水线化(这就要自己设计cuda核函数了)