参加OpenCV的OpenCL模块(以下称OCL)移植工作已经有2个月了。这里我说移植而不是开发,是因为大部分OCL模块的函数是从已经很成熟的GPU模块直接移植过来的。因此,目前阶段OCL模块所支持的函数接口是GPU模块的一个子集,但由于运行平台差别问题,在某些函数上有些细微不同。
OpenCV的版本控制系统已经转移到了git上面(见OpenCV on GitHub),而最新的trunk的master分支也正式加入了OCL模块。今天逛OpenCV的开发者社区时,我发现有人提问在OpenCV库中如何进行使用OCL模块的函数;回答问题的同时,考虑到网上还没有针对OpenCV的OCL模块的资料,我决定写一篇文章简单介绍下OCL模块以方便开发者使用。
Introduction to OpenCL
对于OpenCL已经有所了解的,可以直接跳过这一节。
"OpenCL是用于编写在异构平台上运行程序的框架,所谓异构平台,一般情况我们指GPU和CPU两种处理器混合的平台。OpenCL由一门用于编写kernels (在OpenCL设备上运行的函数)的语言(基于C99)和一组用于定义并控制平台的API组成。" OpenCL可以实现GPGPU(General-purpose computing on graphics processing units, 通用图形处理器)运算, "(GPGPU)是一种利用处理图形任务的GPU来计算原本由CPU处理的通用计算任务。这些通用计算常常与图形处理没有任何关系。由于现代图形处理器强大的并行处理能力和可编程流水线,令流处理器可以处理非图形数据。特别在面对单指令流多数据流(SIMD),且数据处理的运算量远大于数据调度和传输的需要时,通用图形处理器在性能上大大超越了传统的中央处理器应用程序。"摘自wikipedia
简单解释一下这段话中几个重点:
利用GPU强大的并行能力代替CPU进行运算
由于GPU本身特殊的硬件架构,GPU被设计成拥有非常强大的并行运算能力。以OpenCV为例,把GPGPU融入到OpenCV的首要原因是GPU的并行能力特别适合于关于矩阵的运算。利用GPU,我们可以发起很多个轻量级线程,每个线程仅处理一个元素的计算来实现数据并行;而对于CPU,我们只能按顺序每个元素迭代运算。GPU和CPU运算对比起来可以想象成4辆坦克与1万个士兵的战斗力水平的对比;孰胜孰劣,还要看具体进行的任务。因此,并不是所有的OpenCV函数都适合移植到GPU上进行运算;这就是为什么只有一部分的函数被移植到了GPU上运算。
OpenCL由在OpenCL设备上运行的kernel函数语言和控制平台的API组成
OpenCL包含两个主要部分:device和host。在CPU和GPU组成的异构平台中,我们一般把运行核函数的GPU处理器部分称为device,把控制平台API的CPU称为host。相应的,把host上的内存(就是内存)称为host memory;而把device上的内存(例如GPU显存)称为device memory或者device buffer。在OpenCV里,我们把这两种内存封装为cv::Mat
和cv::ocl::oclMat
结构。
数据调度和传输
OpenCV的OCL模块中,在GPU上进行运算之前我们必须把内存转成GPU可以直接调用的显存。而在GPU上的运算结束后,我们还需要将在GPU显存上的数据转移到CPU可用的内存上。这两个操作在oclMat中定义为两个成员函数,分别为