OpenCL
OpenCL(Open Computing Language,开放计算语言)是一个为异构平台编写程序的框架,此异构平台可由CPU、GPU、DSP、FPGA或其他类型的处理器與硬體加速器所组成。OpenCL由一门用于编写kernels(在OpenCL设备上运行的函数)的语言(基于C99)和一组用于定义并控制平台的API组成。OpenCL提供了基于任务分割和数据分割的并行计算机制。
原作者 | 苹果公司 |
---|---|
開發者 | 科纳斯组织 |
首次发布 | 2009年8月28日 |
当前版本 | 3.0.17[1](2024年10月24日) |
编程语言 | C,具有C++绑定 |
操作系统 | Android(厂商依赖)[2]、FreeBSD[3]、Linux、macOS(通过PoCL)、Windows |
平台 | ARMv7、ARMv8[4]、Cell、IA-32、 Power、x86-64 |
类型 | 异构计算API |
许可协议 | OpenCL规范许可证 |
网站 | www |
编程范型 | 指令式(过程式)、结构化、(仅C++)面向对象、泛型 |
---|---|
语言家族 | C |
当前版本 |
|
型態系統 | 静态、弱类型、明示、名义 |
實作語言 | 特定于实现 |
文件扩展名 | .cl .clcpp |
網站 | www |
主要實作產品 | |
AMD、Gallium Compute、IBM、Intel NEO、Intel SDK、Texas Instruments、Nvidia、PoCL、ARM | |
啟發語言 | |
C99、CUDA、C++14、C++17 |
OpenCL类似于另外两个开放的工业标准OpenGL和OpenAL,这两个标准分别用于三维图形和计算机音频方面。OpenCL擴充了GPU圖形生成之外的能力。OpenCL由非盈利性技术组织Khronos Group掌管。
历史
OpenCL最初由苹果公司开发,拥有其商标权,并在与AMD,IBM,Intel和NVIDIA技术团队的合作之下初步完善。随后,苹果将这一草案提交至Khronos Group。2008年6月16日,Khronos的通用计算工作小组成立[5]。5个月后的2008年11月18日,该工作组完成了OpenCL 1.0规范的技术细节[6]。该技术规范在由Khronos成员进行审查之后,于2008年12月8日公开发表[7]。
2010年6月14日,OpenCL 1.1发布[8]。2011年11月15日,OpenCL 1.2发布[9]。2013年11月18日,OpenCL 2.0发布[10]。2015年11月16日,OpenCL 2.1发布[11]。2017年5月16日,OpenCL 2.2发布[12]。
路线图
在2017年5月发行OpenCL 2.2之时,Khronos Group宣布OpenCL将尽可能的汇合于Vulkan,以确使OpenCL软件在这两种API上灵活部署[13]。这已经由Adobe的Premiere Rush展示出来,它使用clspv开源编译器[14],编译了大量OpenCL C内核代码,使其在部署于Android的Vulkan运行时系统上运行[15]。
OpenCL拥有独立于Vulkan的前瞻性路线图[16],即曾意图在2020年发行的“OpenCL Next”[17],它可以集成于扩展诸如Vulkan/OpenCL互操作、Scratch-Pad内存管理、扩展子组、SPIR-V 1.4摄入和SPIR-V扩展调试信息;OpenCL还在考虑类似Vulkan的装载器和分层以及“灵活配置”,以便在多种加速类型上灵活部署。
OpenCL 3.0
在2020年8月30日,发行了最终的OpenCL 3.0规范[18]。OpenCL 1.2功能已经成为强制性基准,而所有OpenCL 2.x和OpenCL 3.0特征变为可选项[19]。这个规范保留了“OpenCL C”语言[20],并废弃了版本2.1介入的“OpenCL C++”内核语言[21],将其替代为“C++ for OpenCL”语言[22],它基于了Clang/LLVM编译器,实现了C++17的子集和SPIR-V中间代码。C++ for OpenCL版本1.0的官方文档在2020年12月发表[23],它后向兼容于OpenCL C 2.0。
在IWOCL 21上发布的OpenCL 3.0.7,提出了C++ for OpenCL的新版本和一些Khronos openCL扩展[24]。在2021年12月,发行了C++ for OpenCL版本2021[25],它完全兼容于OpenCL 3.0标准。NVIDIA密切协作于Khronos OpenCL工作组,通过信号量和内存共享改进了Vulkan互操作[26]。小更新3.0.14版本,具有缺陷修正和针对多设备的一个新扩展[27]。
範例
快速傅立葉變換
// create a compute context with GPU device
context = clCreateContextFromType(NULL, CL_DEVICE_TYPE_GPU, NULL, NULL, NULL);
// create a command queue
queue = clCreateCommandQueue(context, NULL, 0, NULL);
// allocate the buffer memory objects
memobjs[0] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(float)*2*num_entries, srcA, NULL);
memobjs[1] = clCreateBuffer(context, CL_MEM_READ_WRITE, sizeof(float)*2*num_entries, NULL, NULL);
// create the compute program
program = clCreateProgramWithSource(context, 1, &fft1D_1024_kernel_src, NULL, NULL);
// build the compute program executable
clBuildProgram(program, 0, NULL, NULL, NULL, NULL);
// create the compute kernel
kernel = clCreateKernel(program, "fft1D_1024", NULL);
// set the args values
clSetKernelArg(kernel, 0, sizeof(cl_mem),(void *)&memobjs[0]);
clSetKernelArg(kernel, 1, sizeof(cl_mem),(void *)&memobjs[1]);
clSetKernelArg(kernel, 2, sizeof(float)*(local_work_size[0]+1)*16, NULL);
clSetKernelArg(kernel, 3, sizeof(float)*(local_work_size[0]+1)*16, NULL);
// create N-D range object with work-item dimensions and execute kernel
global_work_size[0] = num_entries;
local_work_size[0] = 64;
clEnqueueNDRangeKernel(queue, kernel, 1, NULL, global_work_size, local_work_size, 0, NULL, NULL);
真正的運算:(基於Fitting FFT onto the G80 Architecture)[29]
// This kernel computes FFT of length 1024. The 1024 length FFT is decomposed into
// calls to a radix 16 function, another radix 16 function and then a radix 4 function
__kernel void fft1D_1024(__global float2 *in, __global float2 *out,
__local float *sMemx, __local float *sMemy){
int tid = get_local_id(0);
int blockIdx = get_group_id(0) * 1024 + tid;
float2 data[16];
// starting index of data to/from global memory
in = in + blockIdx; out = out + blockIdx;
globalLoads(data, in, 64); // coalesced global reads
fftRadix16Pass(data); // in-place radix-16 pass
twiddleFactorMul(data, tid, 1024, 0);
// local shuffle using local memory
localShuffle(data, sMemx, sMemy, tid, (((tid & 15)* 65) +(tid >> 4)));
fftRadix16Pass(data); // in-place radix-16 pass
twiddleFactorMul(data, tid, 64, 4); // twiddle factor multiplication
localShuffle(data, sMemx, sMemy, tid, (((tid >> 4)* 64) +(tid & 15)));
// four radix-4 function calls
fftRadix4Pass(data); // radix-4 function number 1
fftRadix4Pass(data + 4); // radix-4 function number 2
fftRadix4Pass(data + 8); // radix-4 function number 3
fftRadix4Pass(data + 12); // radix-4 function number 4
// coalesced global writes
globalStores(data, out, 64);
}
Apple的網站上可以發現傅立葉變換的例子[30]
平行合併排序法
使用 Python 3.x 搭配 PyOpenCL 與 NumPy
import io
import random
import numpy as np
import pyopencl as cl
def dump_step(data, chunk_size):
"""顯示排序過程"""
msg = io.StringIO('')
div = io.StringIO('')
for idx, item in enumerate(data):
if idx % chunk_size == 0:
if idx > 0:
msg.write(' ||')
div.write(' ')
div.write(' --')
else:
msg.write(' ')
div.write('------')
msg.write(' {:2d}'.format(item))
out = msg.getvalue()
if chunk_size == 1: print(' ' + '-' * (len(out) - 1))
print(out)
print(div.getvalue())
msg.close()
div.close()
def cl_merge_sort_sbs(data_in):
"""平行合併排序"""
# OpenCL kernel 函數程式碼
CL_CODE = '''
kernel void merge(int chunk_size, int size, global long* data, global long* buff) {
// 取得分組編號
const int gid = get_global_id(0);
// 根據分組編號計算責任範圍
const int offset = gid * chunk_size;
const int real_size = min(offset + chunk_size, size) - offset;
global long* data_part = data + offset;
global long* buff_part = buff + offset;
// 設定合併前的初始狀態
int r_beg = chunk_size >> 1;
int b_ptr = 0;
int l_ptr = 0;
int r_ptr = r_beg;
// 進行合併
while (b_ptr < real_size) {
if (r_ptr >= real_size) {
// 若右側沒有資料,取左側資料堆入緩衝區
buff_part[b_ptr] = data_part[l_ptr++];
} else if (l_ptr == r_beg) {
// 若左側沒有資料,取右側資料堆入緩衝區
buff_part[b_ptr] = data_part[r_ptr++];
} else {
// 若兩側都有資料,取較小資料堆入緩衝區
if (data_part[l_ptr] < data_part[r_ptr]) {
buff_part[b_ptr] = data_part[l_ptr++];
} else {
buff_part[b_ptr] = data_part[r_ptr++];
}
}
b_ptr++;
}
}
'''
# 配置計算資源,編譯 OpenCL 程式
ctx = cl.Context(dev_type=cl.device_type.GPU)
prg = cl.Program(ctx, CL_CODE).build()
queue = cl.CommandQueue(ctx)
mf = cl.mem_flags
# 資料轉換成 numpy 形式以利轉換為 OpenCL Buffer
data_np = np.int64(data_in)
buff_np = np.empty_like(data_np)
# 建立緩衝區,並且複製數值到緩衝區
data = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=data_np)
buff = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=buff_np)
# 設定合併前初始狀態
data_len = np.int32(len(data_np))
chunk_size = np.int32(1)
dump_step(data_np, chunk_size)
while chunk_size < data_len:
# 更新分組大小,每一回合變兩倍
chunk_size <<= 1
# 換算平行作業組數
group_size = ((data_len - 1) // chunk_size) + 1
# 進行分組合併作業
prg.merge(queue, (group_size,), (1,), chunk_size, data_len, data, buff)
# 將合併結果作為下一回合的原始資料
temp = data
data = buff
buff = temp
# 顯示此回合狀態
cl.enqueue_copy(queue, data_np, data)
dump_step(data_np, chunk_size)
queue.finish()
data.release()
buff.release()
def main():
n = random.randint(5, 16)
data = []
for i in range(n):
data.append(random.randint(1, 99))
cl_merge_sort_sbs(data)
if __name__ == '__main__':
main()
執行結果:
--------------------------------------------------------------------------------------
85 || 41 || 64 || 40 || 90 || 29 || 38 || 41 || 64 || 17 || 20 || 41 || 16 || 65 || 83
-- -- -- -- -- -- -- -- -- -- -- -- -- -- --
41 85 || 40 64 || 29 90 || 38 41 || 17 64 || 20 41 || 16 65 || 83
-------- -------- -------- -------- -------- -------- -------- --
40 41 64 85 || 29 38 41 90 || 17 20 41 64 || 16 65 83
-------------------- -------------------- -------------------- --------------
29 38 40 41 41 64 85 90 || 16 17 20 41 64 65 83
-------------------------------------------- --------------------------------------
16 17 20 29 38 40 41 41 41 64 64 65 83 85 90
--------------------------------------------------------------------------------------
参见
參考文獻
- ^ 1.0 1.1 The OpenCL Specification.
- ^ Android Devices With OpenCL support. Google Docs. ArrayFire. [April 28, 2015].
- ^ FreeBSD Graphics/OpenCL. FreeBSD. [December 23, 2015].
- ^ Conformant Products. Khronos Group. [May 9, 2015].
- ^ Khronos Launches Heterogeneous Computing Initiative (新闻稿). Khronos Group. 2008-06-16 [2008-06-18]. (原始内容存档于2008-06-20).
- ^ OpenCL gets touted in Texas. MacWorld. 2008-11-20 [2009-06-12]. (原始内容存档于2009-02-18).
- ^ The Khronos Group Releases OpenCL 1.0 Specification (新闻稿). Khronos Group. 2008-12-08 [2009-06-12]. (原始内容存档于2010-07-13).
- ^ Khronos Drives Momentum of Parallel Computing Standard with Release of OpenCL 1.1 Specification (新闻稿). Khronos Group. 2010-06-14 [2010-10-13]. (原始内容存档于2010-09-23).
- ^ Khronos Releases OpenCL 1.2 Specification. Khronos Group. November 15, 2011 [June 23, 2015].
- ^ Khronos Finalizes OpenCL 2.0 Specification for Heterogeneous Computing. Khronos Group. November 18, 2013 [February 10, 2014].
- ^ Khronos Releases OpenCL 2.1 and SPIR-V 1.0 Specifications for Heterogeneous Parallel Programming. Khronos Group. November 16, 2015 [November 16, 2015].
- ^ Khronos Releases OpenCL 2.2 With SPIR-V 1.2. Khronos Group. May 16, 2017.
- ^ Breaking: OpenCL Merging Roadmap into Vulkan | PC Perspective. www.pcper.com. [May 17, 2017]. (原始内容存档于November 1, 2017).
- ^ Clspv is a compiler for OpenCL C to Vulkan compute shaders, 2019-08-17 [2024-10-14]
- ^ Vulkan Update SIGGRAPH 2019 (PDF).
- ^ SIGGRAPH 2018: OpenCL-Next Taking Shape, Vulkan Continues Evolving – Phoronix. www.phoronix.com.
- ^ Trevett, Neil. Khronos and OpenCL Overview EVS Workshop May19 (PDF). Khronos Group. May 23, 2019.
- ^ OpenCL 3.0 Specification Finalized and Initial Khronos Open Source OpenCL SDK Released. September 30, 2020.
- ^ OpenCL 3.0 Bringing Greater Flexibility, Async DMA Extensions. www.phoronix.com.
- ^ Munshi, Aaftab; Howes, Lee; Sochaki, Barosz. The OpenCL C Specification Version: 3.0 Document Revision: V3.0.7 (PDF). Khronos OpenCL Working Group. Apr 27, 2020 [Apr 28, 2021]. (原始内容 (PDF)存档于September 20, 2020).
- ^ Sochacki, Bartosz. The OpenCL C++ 1.0 Specification (PDF). Khronos OpenCL Working Group. Jul 19, 2019 [Jul 19, 2019].
- ^ C++ for OpenCL, OpenCL-Guide. GitHub. [2021-04-18] (英语).
- ^ Release of Documentation of C++ for OpenCL kernel language, version 1.0, revision 1 · KhronosGroup/OpenCL-Docs. GitHub. December 2020 [2021-04-18] (英语).
- ^ Trevett, Neil. State of the Union: OpenCL Working Group (PDF): 9. 2021.
- ^ The C++ for OpenCL 1.0 and 2021 Programming Language Documentation. Khronos OpenCL Working Group. Dec 20, 2021 [Dec 2, 2022].
- ^ Using Semaphore and Memory Sharing Extensions for Vulkan Interop with NVIDIA OpenCL. February 24, 2022.
- ^ OpenCL 3.0.14 Released with New Extension for Command Buffer Multi-Device.
- ^ OpenCL (PDF). SIGGRAPH2008. 2008-08-14 [2008-08-14]. (原始内容 (PDF)存档于2012-03-19).
- ^ Fitting FFT onto G80 Architecture (PDF). Vasily Volkov and Brian Kazian, UC Berkeley CS258 project report. May 2008 [2008-11-14]. (原始内容存档 (PDF)于2012-03-19).
- ^ . OpenCL on FFT. Apple. 16 Nov 2009 [2009-12-07]. (原始内容存档于2009-11-30).
外部連結
- 支援OpenCL的產品(页面存档备份,存于互联网档案馆)
- 开源GPU社区(简体中文)