Opencl编程的标准开发流程
前言
在前面的内容中介绍 了opencl编程环境的搭建方式环境搭建,本篇文章以为例讲述opencl编程的标准开发流程
编程环境:VS2015 + Intel® UHD Graphics 620
OpenCL执行流程
1.创建OpenCL程序第一步是要确定其执行的平台,clGetPlatFormIDs:
cl_int clGetPlatFormIDs(cl_uint num_entries, cl_platform _id * platforms,cl_uint *num_platforms)
2.在确定平台之后确定执行计算的设备,使用函数clGetDeviceIDs:
cl_int clGetDeviceIDs(cl_platform_id platform,cl_device_type device_type, cl_uint num_entries, cl_device_id *devices, cl_uint *num_devices);
3.确定设备后创建上下文,上下文为关联的设备、命令队列、程序对象、内核对象提供了一个容器,其所关联的设备必须来自同一平台
。使用函数clCreateContext:
cl_context clCreateContext(const cl_context_properties * properties, cl_uint num_devices, const cl_device_id * devices, (void)(*pfn_notify) (const char * errinfo, const void *private_info ,size_t cb, void *user_data), void *user _data, cl_int * errcode_ret);
4.创建命令队列。在上下文中对对象的操作需要依赖命令队列,主机将命令提交到命令队列中,命令队列将命令发送给设备。命令队列和设备满足一对一
的关系。函数clCreateCommandQueue:
cl_command_queue clCreateCommandQueue(cl_context context, cl_device_id device, cl_command_queue_properties properties,cl_int *errcode_ret)
5.创建内存对象,函数clCreateBuffer:
cl_mem clCreateBuffer(cl_context context,cl_mem_flags flags,size_t size,void *host_ptr,cl_int *errcode_ret)
6.创建程序对象,可以通过传入OpenCL C 源代码文本,也可以利用程序二进制来创建,本人更习惯使用第一种方法,clCreateProgramWithSource:
cl_program clCreateProgramWithSource (cl_context context,cl_uint count,const char **strings,const size_t *lengths,cl_int *errcode_ret)
7.编译程序对象。clBuildProgram 会为指定的所有设备构建程序对象,等价于在C程序上调用编译器与链接器。
cl_int clBuildProgram ( cl_program program,cl_uint num_devices,const cl_device_id *device_list,const char *options,void (CL_CALLBACK *pfn_notify)(cl_program program, void *user_data),void *user_data)
8.创建内核对象。内核对象可以是一个通过命令队列发送到设备上执行的函数,创建函数clCreateKernel:
cl_mem clCreateBuffer(cl_context context,cl_mem_flags flags,size_t size,void *host_ptr,cl_int *errcode_ret)
9.设置内核参数。内核函数的参数需要通过clSetKernelArg来传递。
cl_int clSetKernelArg(cl_kernel kernel, cl_uint arg_index,size_t arg_size, const void *arg_value)
10.执行内核。在所有的准备工作已经做好之后,便可以让设备来执行内核函数,clEnqueueNDRangeKernel:
cl_int clEnqueueNDRangeKernel(cl_command_queue command_queue,cl_kernel kernel, cl_uint work_dim,const size_t *global_work_offset,const size_t *global_work_size,const size_t *local_work_size,cl_uint num_events_in_wait_list,const cl_event *event_wait_list, cl_event *event)
11.OpenCL设备执行完计算后,将数据拷贝回主机端并销毁分配的资源。
以上就是整个的开发流程。
模板
根据此过程,自己编写了一套通用模板
#include<stdio.h>
#include<stdlib.h>
#include<CL/cl.h>
#include<string.h>
#include <iostream>
#pragma warning( disable : 4996 )s
#define MAX_SOURCE_SIZE (0x100000)
int main(void) {
cl_int err = 0;
//查询第一个平台
cl_platform_id platform;
err = clGetPlatformIDs(1, &platform, NULL);
if (err < 0) {
perror("Couldn't find any platforms");
exit(1);
}
//查询平台上的设备
cl_device_id device;
err = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 1, &device, NULL);
if (err < 0) {
perror("Couldn't find any DEVICE");
exit(1);
}
//创建上下文
cl_context context = clCreateContext(NULL, 1, &device, NULL, NULL, &err);
if (NULL == context)
{
perror("Couldn't create context");
exit(1);
}
//创建命令队列
cl_command_queue CommandQueue = clCreateCommandQueue(context,device, 0, NULL);
if (NULL == CommandQueue)
{
perror("Couldn't create command queue");
exit(1);
}
//创建输入输出内存对象
// 创建输入内存对象
int len;
const char* input;//输入字符串
cl_mem memInutBuffer = clCreateBuffer(
context,
CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, // 输入内存为只读,并可以从宿主机内存复制到设备内存
(len + 1) * sizeof(char), // 输入内存空间大小
(void *)input,
NULL);
// 创建输出内存对象
cl_mem memOutputBuffer = clCreateBuffer(
context,
CL_MEM_WRITE_ONLY, // 输出内存只能写
(len + 1) * sizeof(char), // 输出内存空间大小
NULL,
NULL);
if ((NULL == memInutBuffer) || (NULL == memOutputBuffer))
{
perror("Error creating memory objects");
exit(1);
}
//读取CL程序源码,将cl文件中的代码转为字符串
FILE* fp = fopen("*.cl", "rb");
if (fp == NULL) {
perror("Couldn't find the program file");
exit(1);
}
fseek(fp, 0, SEEK_END);
size_t filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
rewind(fp);
char *program_buffer=(char*)malloc(filesize+1);
program_buffer[filesize] = '\0';
fread(program_buffer, sizeof(char), filesize, fp);
fclose(fp);
//创建CL程序
cl_program program= clCreateProgramWithSource(context, 1,&program_buffer,&filesize, &err);
if (err < 0) {
perror("Couldn't create the program");
exit(1);
}
//编译CL程序
const char options[] = "-cl-finite-math-only -cl-no-signed-zeros";
err = clBuildProgram(program, 1, &device, options, NULL, NULL);
if (err < 0) {
/* Find size of log and print to std output */
char *program_log;
size_t log_size;
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG,0, NULL, &log_size);
program_log = (char*)malloc(log_size + 1);
program_log[log_size] = '\0';
clGetProgramBuildInfo(program, device, CL_PROGRAM_BUILD_LOG,log_size + 1, program_log, NULL);
printf("%s\n", program_log);
free(program_log);
exit(1);
}
//创建CL内核
cl_kernel kernel = clCreateKernel(program, "kernelname", &err);
if (err < 0) {
perror("Couldn't create the kernel");
exit(1);
}
//设置内核参数,根据实际的Kernel修改
err = clSetKernelArg(kernel, 0,sizeof(memInutBuffer),(void *)memInutBuffer);
err = clSetKernelArg(kernel, 1, sizeof(memInutBuffer), (void *)memOutputBuffer);
//计算,参数值按照 实际 修改
size_t global_work_size[] = {
100};
size_t local_work_size[] = {
1 };
err = clEnqueueNDRangeKernel(CommandQueue, kernel, 1, NULL,global_work_size, local_work_size,0, NULL, NULL);
//读取结果
err = clEnqueueReadBuffer(CommandQueue, memOutputBuffer, CL_TRUE, 0, len * sizeof(char), memOutputBuffer,0, NULL, NULL);
//输出数据
//释放资源
err = clReleaseKernel(kernel);
err = clReleaseProgram(program);
err = clReleaseMemObject(memInutBuffer);
err = clReleaseMemObject(memOutputBuffer);
err = clReleaseCommandQueue(CommandQueue);
err = clReleaseContext(context);
}
以上均为原创内容 如需转载,请注明出处 如有问题,还请指正。