android+opencv+opencl: cv::dft()的opencl版本的性能分析

在小米mix 2s + 高通骁龙 845 + Adreno 630 上测试了opencl版本的cv::dft()。

测试数据

先看表格里面的描述:

名称 函数名 最大时间(ms) 平均时间(ms) 说明
cpu版本dft cv::dft() - 0.029448 未统计其他,仅cv::dft()函数的调用时间
opencl版本 cv::dft(UMat) 802.557000 0.202941 不计算mat与umat的拷贝,不计算umat的填充对齐
opencl中使用opencl计算的主函数 cv::ocl_dft() 802.553000 0.210583 cv::dft()包装了cv::ocl_dft(),这一层无太多性能损耗
ocl_dft第一步调用的子函数 ocl_dft_rows() 802.518000 0.1031 -
ocl_dft第二步调用的子函数 ocl_dft_cols() 338.004000 0.078061 -
对象池 OCL_FftPlanCache::getInstance().getFftPlan() 0.190000 0.000028 对象池很快,几乎不占用时间,可以忽略
opencl的核函数编译、绑定参数、计算 OCL_FftPlan::enqueueTransform() 464.393000 0.075685 -
核函数编译 enqueueTransform() 464.237000 0.019422 第一次编译很慢,以后会快很多。但是也不用重复编译才对
参数绑定 enqueueTransform() 0.122000 0.016015 绑定参数也很快
核函数执行 enqueueTransform() 1.167000 0.028805 -

结果分析

有这样一些结论:

  • 令人失望:opencl+gpu版本的平均时间 0.202941,而CPU版本的平均时间是 0.029448,GPU版本比CPU版本慢了6.9倍;而且还未加上Mat拷贝到UMat, Mat填充对齐,UMat拷贝回Mat等部分占用的时间;
  • 可以发现,第一次执行cv::dft()的opencl版本的时候,编译核函数很耗时(464ms),后续的编译占用时间尚可;
  • 纯计算时间上看,opencl核函数执行时间大约是0.028805 * 2,大约是CPU版本的1.96倍。产生这样的原因可能是我的测试数据很小,如果数据量很大,GPU版本在纯计算时间上可能会比CPU版本好一些。

优化计划

  • 在调用cv::dft()的opencl版本以前,开一个线程空调用一次cv::ocl_dft(),这样核函数的编译时间就不会占用总调用时间了。
  • ocl::Kernel这里可以建立对象池,而不是每次调用都使用临时对象,这样的话,每次调用可以节约0.019422ms,性能可提升9.6%;
ocl::Kernel k(kernel_name.c_str(), ocl::core::fft_oclsrc, options);
  • 如果采用GPU内存池,每次计算的输入和输出地址都不变,那么参数绑定环节的0.016015ms可以省略,性能可能提升7.9%
  • 在我的cv::dft()的使用场景中,每次连续计算44个矩阵的数据。假设能够找到方法,把44次计算陆续加入队列,让GPU连续计算。假设GPU并发度支持44次计算同时进行,那么GPU版本的理论延迟是 0.202941/44 = 0.004612, 比CPU版本提升 6.39倍!
原文地址:https://www.cnblogs.com/ahfuzhang/p/11097141.html