第二章介绍的都是与加速平台和设备有关的内容,当你的加速设备固定的时候,每次初始化的结果都是一样的,所以实际上每次对于平台的初始化过程都是一样的那几步,只需要照葫芦画瓢就好,后面所说的其实还是按照一定的流程,只是细节上会根据程序的不同有所差别。
OpenCL的程序分为两个部分,第一部分是主机程序,用C\C++编写的用于在主机(电脑)端执行的程序,第二部分是内核程序,用类C语言编写,用于在设备端执行,这种程序一般以.cl为后缀名,下面就介绍一下主机端怎么去调用内核端的程序文件。
cl_program clCreateProgramWithSource (cl_context context, //执行程序的设备所在的上下文
cl_uint count, //程序文件的个数(可能有多个.cl文件)
const char **strings, //程序文件的内容字符串列表
const size_t *lengths, //每个程序文件的长度组成的列表
cl_int *errcode_ret) //错误指示代码
通过函数名可以知道该接口是通过直接读取程序文件来创建程序对象,一个工程可能会用到多个.cl程序文件,因此可以同时根据多个程序文件来创建我们的程序对象,程序对象的类型是cl_program,与设备对象平台对象一样,该类型的变量存放了程序的相关信息。
cl_program clCreateProgramWithBinary (cl_context context, //执行程序的设备所在的上下文
cl_uint num_devices, //目标设备的数量
const cl_device_id *device_list, //目标设备ID列表
const size_t *lengths, //二进制文件长度列表
const unsigned char **binaries, //二进制文件内容字符串列表
cl_int *binary_status, //每个二进制文件创建程序的状态列表
cl_int *errcode_ret) //错误指示代码
有些平台设备例如FPGA是不支持源码创建程序的,一般这种平台都会提供独立的编译工具,将程序文件通过平台指定的编译程序编译后会生成特定格式的二进制文件,然后主机可以通过这些二进制文件创建程序对象。
cl_program clCreateProgramWithBuiltInKernels (cl_context context, //执行程序的设备所在的上下文
cl_uint num_devices, //目标设备的数量
const cl_device_id *device_list, //目标设备ID列表
const char *kernel_names, //内核名称,多个内核用分号隔开
cl_int *errcode_ret) //错误指示代码
这个接口的功能是根据特定的内核来创建程序,一般这个接口很少使用
cl_int clGetProgramInfo (cl_program program, //想要查询信息的程序对象
cl_program_info param_name, //想要查询的信息名称
size_t param_value_size, //传入用于存放信息的参数的大小
void *param_value, //传入用于存放信息的参数
size_t *param_value_size_ret) //返回用于存放信息的参数的大小
与之前一样,想要查询程序的相关信息需要调用上面的接口,可以查询的信息内容有:
用源码创建程序的方法:
char *readKernelSourceFile(const char *filename, size_t *filelength){
FILE *file = NULL;
size_t source_length;
char *source_string;
int ret;
file = fopen(filename, "rb");
if (file == NULL){
//printf("%s at %d :Can't open %s\n", __FILE__, __LINE__ - 2, filename);
return NULL;
}
fseek(file, 0, SEEK_END);
source_length = ftell(file);
fseek(file, 0, SEEK_SET);
source_string = (char *)malloc(source_length + 1);
//source_string[source_length] = '\0';
ret = fread(source_string, source_length, 1, file);
if (ret == 0){
printf("%s at %d :Can't read source %s \n", __FILE__, __LINE__ - 2, filename);
return NULL;
}
fclose(file);
if (source_length != 0){
*filelength = source_length;
}
source_string[source_length] = '\0';
return source_string;
}
size_t file_length;
char * files_string = readKernelSourceFile(filesname, &file_length);
cout << "Input file's content is : " << string((char *)files_string) << endl;
cl_program program = clCreateProgramWithSource(context, 1, (const char **)&files_string, &file_length, &err);
checkError(err, "Failed to create program with source");
char *filestring = new char[file_length];
clGetProgramInfo(program, CL_PROGRAM_SOURCE, NULL, NULL, &file_length);
clGetProgramInfo(program, CL_PROGRAM_SOURCE, file_length, filestring, NULL);
cout << "Output file's content is : " << string(filestring) << endl;
用二进制文件创建程序的方法:
unsigned char *loadBinaryFile(const char *filename, size_t *size){
FILE* fp;
if (fopen_s(&fp, filename, "rb") != 0) {
return NULL;
}
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
unsigned char *binary = new unsigned char[*size];
rewind(fp);
if (fread((void*)binary, *size, 1, fp) == 0) {
delete[] binary;
fclose(fp);
return NULL;
}
return binary;
}
size_t binary_lengths;
unsigned char * files_string = readBinaryFile(filesname2, &binary_lengths);
cout << "Input file's content is : " << string((char *)files_string) << endl;
cl_int *binary_status = new cl_int[device_num];
cl_program program = clCreateProgramWithBinary(context, device_num, devices, &binary_lengths,(const unsigned char **)&files_string, binary_status, &err);
checkError(err, "Failed to create program with source");
unsigned char **binary_file = new unsigned char *[1];
binary_file[0] = new unsigned char[binary_lengths];
clGetProgramInfo(program, CL_PROGRAM_BINARIES, binary_lengths, binary_file, NULL);
cout << "Output file's content is : " << string((char *)binary_file[0]) << endl;
与上下文和命令队列一样,程序对象也有引用计数这样一个参数,调用接口为:
cl_int clRetainProgram (cl_program program)
cl_int clReleaseProgram (cl_program program)
实验代码:
main.cpp
#include"opencl.h"
#define filesname "hello_world.cl"
#define filesname2 "vector.aocx"
char *readKernelSourceFile(const char *filename, size_t *filelength){
FILE *file = NULL;
size_t source_length;
char *source_string;
int ret;
file = fopen(filename, "rb");
if (file == NULL){
//printf("%s at %d :Can't open %s\n", __FILE__, __LINE__ - 2, filename);
return NULL;
}
fseek(file, 0, SEEK_END);
source_length = ftell(file);
fseek(file, 0, SEEK_SET);
source_string = (char *)malloc(source_length + 1);
//source_string[source_length] = '\0';
ret = fread(source_string, source_length, 1, file);
if (ret == 0){
printf("%s at %d :Can't read source %s \n", __FILE__, __LINE__ - 2, filename);
return NULL;
}
fclose(file);
if (source_length != 0){
*filelength = source_length;
}
source_string[source_length] = '\0';
return source_string;
}
unsigned char *readBinaryFile(const char *filename, size_t *size){
FILE* fp;
if (fopen_s(&fp, filename, "rb") != 0) {
return NULL;
}
fseek(fp, 0, SEEK_END);
*size = ftell(fp);
unsigned char *binary = new unsigned char[*size];
rewind(fp);
if (fread((void*)binary, *size, 1, fp) == 0) {
delete[] binary;
fclose(fp);
return NULL;
}
return binary;
}
int main(){
cl_uint platform_num;
clGetPlatformIDs(0, NULL, &platform_num);
cl_platform_id *platforms = new cl_platform_id[platform_num];
clGetPlatformIDs(platform_num, platforms, NULL);
cl_uint device_num;
clGetDeviceIDs(platforms[1], CL_DEVICE_TYPE_ALL, 0, NULL, &device_num);
cl_device_id *devices = new cl_device_id[device_num];
clGetDeviceIDs(platforms[1], CL_DEVICE_TYPE_ALL, device_num, devices, NULL);
cl_int err;
cl_context context = clCreateContext(NULL, device_num, devices, NULL, NULL, &err);
checkError(err, "Can't create context");
cl_command_queue *queues = new cl_command_queue[device_num];
for (cl_uint i = 0; i < device_num; i++){
queues[i] = clCreateCommandQueue(context, devices[i], CL_QUEUE_PROFILING_ENABLE, &err);
checkError(err, "Can't create queue");
}
size_t file_length;
char * files_string = readKernelSourceFile(filesname, &file_length);
cout << "Input file's content is : " << string((char *)files_string) << endl;
cl_program program = clCreateProgramWithSource(context, 1, (const char **)&files_string, &file_length, &err);
checkError(err, "Failed to create program with source");
char *filestring = new char[file_length];
clGetProgramInfo(program, CL_PROGRAM_SOURCE, NULL, NULL, &file_length);
clGetProgramInfo(program, CL_PROGRAM_SOURCE, file_length, filestring, NULL);
cout << "Output file's content is : " << string(filestring) << endl;
/*size_t binary_lengths;
unsigned char * files_string = readBinaryFile(filesname2, &binary_lengths);
cout << "Input file's content is : " << string((char *)files_string) << endl;
cl_int *binary_status = new cl_int[device_num];
cl_program program = clCreateProgramWithBinary(context, device_num, devices, &binary_lengths,
(const unsigned char **)&files_string, binary_status, &err);
checkError(err, "Failed to create program with source");
unsigned char **binary_file = new unsigned char *[1];
binary_file[0] = new unsigned char[binary_lengths];
clGetProgramInfo(program, CL_PROGRAM_BINARIES, binary_lengths, binary_file, NULL);
cout << "Output file's content is : " << string((char *)binary_file[0]) << endl;*/
return 0;
}
hello_world.cl
__kernel void hello_world(){
}
vector.aocx
是FPGA编译出来的可执行二进制文件