Opencl编程的标准开发流程

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);

}

以上均为原创内容 如需转载,请注明出处 如有问题,还请指正。

猜你喜欢

转载自blog.csdn.net/qq_41228014/article/details/114360020