【OpenCL编程任务一】输出进行运算设备的名称

来源:互联网 发布:安德罗妮淘宝 编辑:程序博客网 时间:2024/05/16 08:30

前言

最近在学习OpenCL,想通过一些实例,进行快速入门与上手。

任务描述

输出进行运算设备的名称。

思路

1、具体细化这个任务。我们肯定首先要获取到这个设备的ID或是(地址),然后将其转换为OpenCL可用于输出的格式,将其输出。首先使用clGetPlatformIDs函数,这个命令会得到可用的OpenCL平台的列表。如果参数platforms为NULL,clGetPlatformIDs会返回可用平台数。返回的平台数可以用num_entries来限制,这个参数要大于0并小于或等于可用平台数。

GetPlatformIDs(cl_uint          /* num_entries */,                   cl_platform_id * /* platforms */,                   cl_uint *        /* num_platforms */) CL_API_SUFFIX__VERSION_1_0;  

我们可以这么用,这个时候num其实得到了平台数。err用于判断是否成功执行了,如果成功会返回CL_SUCCESS:

cl_int err;      cl_uint num;      err = clGetPlatformIDs(0, 0, &num);      if (err != CL_SUCCESS) {          std::cerr << "Unable to get platforms\n";          return 0;      }  

2、得到了平台数还不够,我们要获得ID。这个时候再次用这个clGetPlatformIDs函数,只要platform不设置成NULL,那么就可以得到ID了对不对?下面的代码中,ID被存放在了vector容器中。

std::vector<cl_platform_id> platforms(num);      err = clGetPlatformIDs(num, &platforms[0], &num);      if (err != CL_SUCCESS) {          std::cerr << "Unable to get platform ID\n";          return 0;      }  

3、得到了ID,我们要想办法输出。(这个地方我也没太懂)引用一下:在OpenCL中,是透过一个property的阵列,以「property种类」及「property内容」成对出现,并以0做为结束。所以,要先建立一个阵列(数组),然后由于要指定的property是cl_context_platform,但是刚刚取得的(看上面)
是cl_platform_id对不对(即platforms[0])?所以要用一个C++的强制转换符reinterpret_cast来进行转换。它的使用形式大概如下:

 reinterpret_cast<目标格式>(数据)  

回到上文。我们转换数据后,要将其转换为阵列(数组),所以命名一个cl_context_properties的阵列,最后一项是0,第一项,种类,也就是CL_CONTEXT_PLATFORM,第二项,内容,即转换后的数据:

cl_context_properties prop[] = { CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>(platforms[0]), 0 };  

4、有了阵列,我们就可以建立一个context来准备输出了。用clCreateContextFromType这个函数,其定义如下:第一个参数,就是我们建立好的阵列;第二个参数,采用什么设置进行运算。

clCreateContextFromType(const cl_context_properties * /* properties */,                          cl_device_type          /* device_type */,                          void (CL_CALLBACK *     /* pfn_notify*/ )(const char *, const void *, size_t, void *),                          void *                  /* user_data */,                          cl_int *                /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0;  

第三个参数是一个回调函数,用于传递错误信息。这个回调函数将由OpenCL实现用于报告在这方面出现的错误信息,可以由OpenCL实现异步调用,以确保回调函数是线程安全的。如果pfn_notify是空的,就没有注册回调函数。

第四个参数是当第三个参数被定义(注册)时才用到的,用于传递数据。如果上面为NULL,那么这里也为NULL。
第五个参数,返回一个合适的错误代码。如果errcode_ret是无效的,那么不返回错误代码。
所以,我们这么来定义:

cl_context context = clCreateContextFromType(prop, CL_DEVICE_TYPE_DEFAULT, NULL, NULL, NULL);      if (context == 0) {          std::cerr << "Can't create OpenCL context\n";          return 0;      }  

采用默认计算装置,还有几种可以选,包括:
CL_DEVICE_TYPE_CPU:使用CPU 装置
CL_DEVICE_TYPE_GPU:使用显示晶片装置
CL_DEVICE_TYPE_ACCELERATOR:特定的OpenCL 加速装置,例如CELL
CL_DEVICE_TYPE_DEFAULT:系统预设的OpenCL 装置
CL_DEVICE_TYPE_ALL:所有系统中的OpenCL 装置

4、我们得到了一个context,但是它可以包含一个或者多个装置。所以接下来的工作就是获得要取得装置的列表。使用clGetContextInfo函数。

clGetContextInfo(cl_context         /* context */,                    cl_context_info    /* param_name */,                    size_t             /* param_value_size */,                    void *             /* param_value */,                    size_t *           /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;  
clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &cb);  

如上来使用,这个函数相对而言较为简单,它和第一个函数clGetPlatformIDs有点类似,调用两次,第一次取得列表的字节量保存在 size_t cb中。
接着我们想知道到底有几个装置?其实数学公式就是cb/id的固定字节数。那么第二次调用这个函数,获取所有device的列表。

 clGetContextInfo(context, CL_CONTEXT_DEVICES, cb, &devices[0], 0);

5、将其转换为字符串,输出,可以使用

clGetDeviceInfo(cl_device_id    /* device */,                  cl_device_info  /* param_name */,                   size_t          /* param_value_size */,                   void *          /* param_value */,                  size_t *        /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0;  

当size_t 不为0时,这个函数将会把第四个参数所指的地方指向device的CL_DEVICE_NAME所在地址。如果我们把一个string类型的变量进行操作,那么就可以使用cout进行输出。(char*自然也可以)

最终程序

#include <iostream>  #include <string>  #include <vector>  #include <OpenCL/opencl.h>  #include <CL/cl.h>  int main()  {      cl_int err;      cl_uint num;      err = clGetPlatformIDs(0, 0, &num);      if (err != CL_SUCCESS) {          std::cerr << "Unable to get platforms\n";          return 0;      }      std::vector<cl_platform_id> platforms(num);      err = clGetPlatformIDs(num, &platforms[0], &num);      if (err != CL_SUCCESS) {          std::cerr << "Unable to get platform ID\n";          return 0;      }      cl_context_properties prop[] = { CL_CONTEXT_PLATFORM, reinterpret_cast<cl_context_properties>(platforms[0]), 0 };      cl_context context = clCreateContextFromType(prop, CL_DEVICE_TYPE_DEFAULT, NULL, NULL, NULL);      if (context == 0) {          std::cerr << "Can't create OpenCL context\n";          return 0;      }      size_t cb;      clGetContextInfo(context, CL_CONTEXT_DEVICES, 0, NULL, &cb);      std::vector<cl_device_id> devices(cb / sizeof(cl_device_id));      clGetContextInfo(context, CL_CONTEXT_DEVICES, cb, &devices[0], 0);      clGetDeviceInfo(devices[0], CL_DEVICE_NAME, 0, NULL, &cb);      std::string devname;      devname.resize(cb);      clGetDeviceInfo(devices[0], CL_DEVICE_NAME, cb, &devname[0], 0);      std::cout << "Device: " << devname.c_str() << "\n";      clReleaseContext(context);      std::cin.get();      return 0;  }  

感想

第一个感想,OpenCL存在着大量的套路操作:先呼叫某个函数,获取空间大小,把自己的资源进行调整后,第二次呼叫,取得想要的数据。第二个感想,OpenCL真尼玛麻烦…

1 0