前言
随着 AI
的兴起,AI
相关的软件栈越来越受到重视。在训练 AI
模型和使用 AI
模型进行推理时,涉及大量的、可并行的基础数学运算,这就使得专为并行计算而生的图形处理器和各种计算加速卡在最近十来年里大放异彩,获得了大量关注。
对用户和上层软件开发人员来说,软件的可移植性是一件非常重要的事情,因为用户不想被绑定到某一个硬件厂商上,用户在必要的时候想更换他们所使用的硬件设备,比如从 Nvidia
的显卡换成 AMD
的显卡,理想情况下,在这个过程中,与之配套的软件栈应该也能顺利切换,再比如,上层软件开发人员,也希望他们的软件能同时支持多个硬件平台,以便能够覆盖更多的用户。要实现上面两点,规范和标准的重要性不言而喻。
OpenCL 发展历史
2008
年,苹果公司向 Khronos Group
提交了一份关于跨平台计算框架的草案,该草案由苹果公司开发,并与 AMD
、IBM
、Intel
和 NVIDIA
公司合作逐步完善。这个跨平台计算框架就是OpenCL
(Open Computing Language
, 开放计算语言)。OpenCL
通过定义一套机制来实现硬件独立的软件开发环境,并且能将执行代码有效映射到不同的硬件平台上执行,以屏蔽异构硬件系统和指令集之间差异,并简化了异构计算编程复杂度。
OpenCL 规范
目前,OpenCL
规范还是由 Khronos
组织进行维护,且还在一直演进中,最近的一个版本是 2024
年 4
月份发布的 v3.0.16
。OpenCL
相关的规范主要包括:OpenCL API
规范、OpenCL C
语言规范、OpenCL
扩展规范、OpenCL SPIR-V
规范等。最主要的两个规范是OpenCL API
规范和 OpenCL C
语言规范,即主机侧规范和设备侧规范。
定义主机侧规范和设备侧规范,是为了方便应用程序(使用 OpenCL
主机侧 API
开发的程序)和核函数(使用 OpenCL C
语言开发的程序)在不同平台上进行迁移。
OpenCL
主机侧规范:https://registry.khronos.org/OpenCL/specs/3.0-unified/pdf/OpenCL_API.pdf
OpenCL
设备侧规范:https://registry.khronos.org/OpenCL/specs/3.0-unified/pdf/OpenCL_C.pdf
OpenCL 主机侧程序示例
下面是一个典型的 OpenCL
主机侧 Hello World
程序,它描述了主机侧 OpenCL
程序的一般性流程:
int main(int argc, char **argv)
{
clGetPlatformIDs([...]); // 查询系统中可用的platforms
clGetDeviceIDs([...]); // 查询系统中可用的设备
clCreateContext([...]); // 创建一个运行上下文
clCreateCommandQueue([...]); // 创建一个命令队列
clCreateProgramWithSource([...]); // 基于用户的程序源码创建一个程序对象
clBuildProgram([...]); // 编译OpenCL用户程序
clCreateKernel([...]); // 基于编译后的程序对象创建一个kernel对象
clCreateBuffer([...]); // 创建一个内存buffer
clSetKernelArg([...]); // 设置核函数的运行参数
clEnqueueNDRangeKernel([...]); // 将核函数入队列
clFinish([...]); // 等待命令执行
clEnqueueReadBuffer([...]); // 通过命令从buffer对象中读取内容到host内存中
}
OpenCL 开源实现
很多主要的软硬件厂商都曾推出过 OpenCL
开源实现,包括:
- Intel:Beignet(已停止维护)、compute-runtime
- AMD:ROCm
- Mesa社区:Clover、Rusticl
- ARM:Shamrock
- Nvidia:CUDA Toolkits
- PoCL社区:PoCL
OpenCL 与 CUDA 的比较
CUDA
是 NVIDIA 2006
年推出的一个异构并行编程框架,比 OpenCL
还早两年,因此 OpenCL
规范在演进的过程中也借鉴了 CUDA
的一些思想。总的来说,由于 OpenCL
生态的碎片化,使得 OpenCL
的应用没有 CUDA
广泛,这一点可以从各种 AI
上层软件库对 CUDA
和 OpenCL
的支持力度可以看到。从支持的软硬件特性来看,CUDA
也比 OpenCL
更加先进一些。