No.1 HelloOpenCL

概述

使用 OpenCL 实现将大写字母转换为小写字母,然后输出。例如,输入字符串为Hello OpenCL, I like U,经转换后的输出是hello opencl, i like u。完整代码参见 No.1_HelloOpenCL,具体执行流程如下图所示:

这里写图片描述

该 OpenCL 程序在华为荣耀8 上执行,输出内容如下:

shell@HWFRD:/data/local/tmp $ ./hello_opencl
[Platform Infomation]
platform name: ARM Platform

[Device Infomation]
device name: Mali-T880

[Result]
lower case is: hello opencl, i like u

实现

1.获取并选择一个将要运行的平台

平台包含了厂商对应的软、硬件环境,用户可以在上面运行自己开发的 OpenCL 程序。
OpenCL 支持跨平台,因此同一环境下可能同时存在多个平台,这时就需要获取当前环境下支持 OpenCL 的平台有哪些,并选择需要使用的进行后续初始化。

err = clGetPlatformIDs(1, &platform, NULL);

2.获取执行 OpenCL 程序的设备

OpenCL 基于 C99 的扩展,它最终会被编译、链接。同一平台上可能包含多个可以执行 OpenCL 内核程序的设备,这些设备可以是 GPU 或者 CPU。在执行 OpenCL 内核之前,需要选择运行的设备。首先查看是否包含可用 GPU 设备,如果没有则选择 CPU。

err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);
if (err != CL_SUCCESS) {
        printf("can't get gpu device, try cpu...\n");
        err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_CPU, 1, &device, NULL);
}

3.创建上下文

上下文用来管理不同的软硬件资源,这和操作系统中进程的概念有些类似,它包含了程序运行状态的资源。在 OpenCL 中,一个上下文只能对应到一个特定的平台,不同的平台需要创建自己的上下文。

context = clCreateContext(NULL, 1, &device, NULL, NULL, &err);
if (context == NULL) {
        printf("create context fail\n");
        exit(EXIT_FAILURE);
}

4.创建命令队列

当设备执行任务时,由主机来提交命令,将命令发送到队列中。命令队列和设备对应,一个命令队列只能关联到一个设备,但是单个设备可以有多个命令队列,这就可以将不同的命令提交到不同队列。

queue = clCreateCommandQueue(context, device, 0, &err);
if (queue == NULL) {
        printf("create command queue fail\n");
        exit(EXIT_FAILURE);
}

5.创建程序

通过源代码创建程序对象,以供后续编译、链接。

program = clCreateProgramWithSource(context, 1, &source, NULL, &err);
if (program == NULL) {
        printf("create program fail\n");
        exit(EXIT_FAILURE);
}

6.构建程序

程序对象经过编译、链接后,可以在指定的设备上运行,该过程实质调用了编译器和连接器。主机端的程序在运行之前需要构建,同理,设备端的内核程序也需在提交给设备运行之前构建。如果构建错误,则获取相关信息,以便进一步定位问题所在。

err = clBuildProgram(program, 1, &device, NULL, NULL, NULL);
if (CL_SUCCESS != err) {
        size_t bufSize = 1024;
        char buf[bufSize];

        err = clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG,
                bufSize, buf, NULL);
        printf("build log:\n%s\n", buf);
        exit(EXIT_FAILURE);
}

7.创建内存对象

创建两个内存对象。输入内存对象引用的全局内存区域用来存放包含大写字母的字符串,经转换后小写字母存放到输出内存引用的内存区域。全局内存区域中的数据使用内存对象来表示,一个内存对象就是对全局内存区域的引用。

input = clCreateBuffer(context, CL_MEM_READ_ONLY |
        CL_MEM_COPY_HOST_PTR, strlen(upper_case), upper_case, &err);
output = clCreateBuffer(context, CL_MEM_WRITE_ONLY, strlen(upper_case),
        NULL, &err);
if (input == NULL || output == NULL) {
        printf("create memory buffer fail: %d\n", err);
        exit(EXIT_FAILURE);
}

8.创建内核

内核是设备程序的入口,如图形处理器中的着色器程序。它类似于库文件中导出的库函数。设备执行的程序,以内核对象的方式提交到命令队列中。

kernel = clCreateKernel(program, "tolower", &err);
if (kernel == NULL) {
        printf("create kernel fail: %d\n", err);
        exit(EXIT_FAILURE);
}

9.设置内核参数

给内核程序中的每个参数指定对应的值。

err = clSetKernelArg(kernel, 0, sizeof(cl_mem), &input);
err |= clSetKernelArg(kernel, 1, sizeof(cl_mem), &output);

10.执行内核

将内核对象提交到命令队列中,供设备执行。

err = clEnqueueNDRangeKernel(queue, kernel, 1,
                0, g_size, local_size,
                0, NULL, NULL);

参数 local_work_size 可以为 NULL。在这种情况下,由 OpenCL 的实现来决定如何把全局工作项划分为更小的工作组。

11.清空队列

避免获取的结果异常,等待直到命令队列中的命令在设备端执行完。

clFinish(queue);

12.获取结果

获取设备端返回的结果。

13.释放资源

在程序退出之前,释放前面创建的对象资源。

clReleaseKernel(kernel);
clReleaseMemObject(input);
clReleaseMemObject(output);
clReleaseProgram(program);
clReleaseCommandQueue(queue);
clReleaseContext(context);

内核

__kernel 代表这是一个内核函数。它包含两个参数,它们是指向全局内存区域的指针。in 指向的内存区域由 input 内存对象来引用,out 指向的内存区域由 output 内存对象来引用。每个工作项在全局范围内都有一个唯一的标识,通过该标识就可以将缓冲区中的每个元素和执行的内核实例关联。

const char *source =
"__kernel void tolower(__global char *in, __global char *out)    \n"
"{                                                               \n"
"   int g_id = get_global_id(0);                                 \n"
"                                                                \n"
"   if ((in[g_id] >= 'A') && (in[g_id] <= 'Z'))                  \n"
"       out[g_id] = in[g_id] + 32;                               \n"
"   else                                                         \n"
"       out[g_id] = in[g_id];                                    \n"
"}                                                               \n";

小结

使用 clCreateXXXX 创建的对象,在程序退出前需要释放对应的资源。EXIT_FAILUREstdlib.h 中定义,通常作为 exit() 函数参数来表明程序的运行结果。

### 如何在ARM RK3588平台上使用OpenCL 为了在ARM RK3588平台成功部署和利用OpenCL进行异构计算,需完成几个关键步骤。这些步骤涉及软件栈的准备、驱动程序安装以及编写简单的OpenCL应用程序验证环境设置。 #### 安装必要的依赖项和支持包 确保操作系统已更新至最新状态,并安装构建工具链和其他必需的支持库。对于Ubuntu 20.04而言,可以通过apt-get命令获取所需的开发文件[^2]: ```bash sudo apt update && sudo apt upgrade -y sudo apt install ocl-icd-opencl-dev opencl-headers clinfo ``` 上述指令不仅会下载OpenCL头文件及其ICD加载器,还会提供`clinfo`实用程序用于查询当前系统的OpenCL能力。 #### 获取并编译Rockchip提供的Mali GPU驱动 由于RK3588集成了Mail-G610 MP4图形处理器,因此需要特定版本的开源或闭源驱动才能激活其完整的硬件特性。可以从官方渠道获得适用于Linux内核的预编译二进制文件或者按照文档说明自行编译最新的分支版本[^4]。 一旦安装完毕,重启设备使更改生效;之后应该能够通过执行`clinfo`看到至少一个可用的OpenCL平台条目,表明GPU已被识别为潜在的计算资源之一。 #### 编写基本的OpenCL Hello World应用 下面给出一段简易代码片段作为入门示例,展示怎样创建上下文、队列并与选定的目标设备交互: ```cpp #include <CL/cl.hpp> #include <iostream> int main() { std::vector<cl::Platform> platforms; cl::Platform::get(&platforms); if(platforms.empty()){ std::cerr << "No platforms found." << std::endl; return EXIT_FAILURE; } auto platform = platforms.front(); std::cout << "Using platform: "<< platform.getInfo<CL_PLATFORM_NAME>()<< "\n"; std::vector<cl::Device> devices; platform.getDevices(CL_DEVICE_TYPE_GPU | CL_DEVICE_TYPE_CPU , &devices); if(devices.empty()) { std::cerr << "No device found." << std::endl; return EXIT_FAILURE; } try{ cl::Context context(devices); cl::CommandQueue queue(context, devices[0]); const char* kernelSource = "__kernel void hello(__global int *output) {" "*output = 42;" "}"; cl::Program::Sources sources(1,std::make_pair(kernelSource,strlen(kernelSource))); cl::Program program(context,sources); program.build("-cl-std=CL1.2"); int result = 0; cl::Buffer buffer(context,sizeof(int),nullptr,&result); cl::Kernel kernel(program,"hello"); kernel.setArg(0,buffer); queue.enqueueNDRangeKernel(kernel,cl::NullRange,cl::NDRange(1)); queue.finish(); queue.enqueueReadBuffer(buffer,CL_TRUE,0,sizeof(result),&result); std::cout<<"Result from OpenCL Kernel:"<<result<<"\n"; }catch(const cl::Error&e){ std::cerr <<"ERROR:" <<e.what()<< "("<< e.err()<<")\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } ``` 这段C++代码展示了如何初始化OpenCL运行时环境,定义简单算术逻辑单元(ALU),并通过主机端API调用来触发远程执行流程。注意这里假设存在兼容的GPU/CPU节点可供调度作业任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值