android+opencv+opencl: cv::dft()的opencl版本的性能分析
阅读原文时间:2023年07月08日阅读:3

在小米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倍!