OpenCL

OpenCL（Open Computing Language，开放计算语言）是一个为异构平台编写程序的框架，此异构平台可由CPU、GPU、DSP、FPGA或其他类型的处理器與硬體加速器所组成。OpenCL由一门用于编写kernels（在OpenCL设备上运行的函数）的语言（基于C99）和一组用于定义并控制平台的API组成。OpenCL提供了基于任务分割和数据分割的并行计算机制。 OpenCL类似于另外两个开放的工业标准OpenGL和OpenAL，这两个标准分别用于三维图形和计算机音频方面。OpenCL擴充了GPU圖形生成之外的能力。OpenCL由非盈利性技术组织Khronos Group掌管。 OpenCL最初由苹果公司开发，拥有其商标权，并在与AMD，IBM，Intel和NVIDIA技术团队的合作之下初步完善。随后，苹果将这一草案提交至Khronos Group。2008年6月16日，Khronos的通用计算工作小组成立。5个月后的2008年11月18日，该工作组完成了OpenCL 1.0规范的技术细节。该技术规范在由Khronos成员进行审查之后，于2008年12月8日公开发表。 2010年6月14日，OpenCL 1.1发布。2011年11月15日，OpenCL 1.2发布。2013年11月18日，OpenCL 2.0发布。2015年11月16日，OpenCL 2.1发布。2017年5月16日，OpenCL 2.2发布。 在2017年5月发行OpenCL 2.2之时，Khronos Group宣布OpenCL将尽可能的汇合于Vulkan，以确使OpenCL软件在这两种API上灵活部署。这已经由Adobe的Premiere Rush展示出来，它使用clspv开源编译器，编译了大量OpenCL C内核代码，从而在部署于Android的Vulkan运行时系统上运行。 OpenCL拥有独立于Vulkan的前瞻性路线图，即曾意图在2020年发行的“OpenCL Next”，它可以集成于扩展比如Vulkan/OpenCL互操作、Scratch-Pad内存管理、扩展子组、SPIR-V 1.4摄入和SPIR-V扩展调试信息；OpenCL还在考虑类似Vulkan的装载器和分层以及“灵活配置”，以便在多种加速类型上灵活部署。 在2020年8月30日，发行了最终的OpenCL 3.0规范。OpenCL 1.2功能已经成为强制性基准，而所有OpenCL 2.x和OpenCL 3.0特征变为可选项。这个规范保留了“OpenCL C”语言，并废弃了版本2.1介入的“OpenCL C++”内核语言，将其替代为“C++ for OpenCL”语言，它基于了Clang/LLVM编译器，实现了C++17的子集和SPIR-V（英语：Standard Portable Intermediate Representation）中间代码。C++ for OpenCL版本1.0的官方文档在2020年12月发表，它后向兼容于OpenCL C 2.0。 在IWOCL（英语：IWOCL） 21上发布的OpenCL 3.0.7，提出了C++ for OpenCL的新版本和一些Khronos openCL扩展。在2021年12月，发行了C++ for OpenCL版本2021，它完全兼容于OpenCL 3.0标准。NVIDIA密切协作于Khronos OpenCL工作组，通过信号量和内存共享改进了Vulkan互操作。小更新3.0.14版本，具有缺陷修正和针对多设备的一个新扩展。 一個快速傅立葉變換的式子： // 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） // 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的網站上可以發現傅立葉變換的例子 使用 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 -------------------------------------------------------------------------------------- 信息技术主题 GPGPU CUDA DirectCompute C++ AMP 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社区（简体中文）