OpenCL异构并行计算编程笔记(1):平台、设备与上下文

       OpenCL(全称Open Computing Language,开放运算语言)是第一个面向异构系统通用目的并行编程的开放式、免费标准,也是一个统一的编程环境,便于软件开发人员为高性能计算服务器、桌面计算系统、手持设备编写高效轻便的代码,而且广泛适用于多核心处理器(CPU)、图形处理器(GPU)、Cell类型架构以及数字信号处理器(DSP)等其他并行处理器[1]。

       需要注意的是,不同于其他第三方库,OpenCL只提供一个开放API的规范,仅仅定义了接口,但并没有进行实现,各个接口的实现由处理器生产厂商自己完成。所以,虽然包括Intel、AMD以及Nvidia在内的厂商都对OpenCL提供了支持,但其实现的方法却各有不同,使得在不用品牌的处理器上使用OpenCL时需要使用特定的SDK,同时在特性上会略微有所不同(但基本不会产生太大影响)。接下来就简单介绍一下基于OpenCL的异构编程方法,在进行OpenCL编程之前,最好先了解OpenCL的架构模型,具体资料可参考:从零开始学习OpenCL开发架构,在此不予赘述。(实例环境:Windows10、Visual Studio 2015、OpenCL 1.1、Intel Core i5 3437U、Nvidia GeForce 840M)


一、获取平台(Platform):

       不同厂商对于OpenCL具体的实现不同,某个厂商对于OpenCL的实现就形成了一个平台。不同厂商的处理器只能在不同的平台上运行,换句话说一个平台上只用运行实现该平台厂商的处理器。例如Intel、AMD和Nvidia均对OpenCL进行了自己的实现,在Intel的平台上可以同时使用Intel的CPU和GPU,但无法使用AMD和Nvidia的处理器;同样在AMD的平台上无法使用Intel和Nvidia二者的设备。在进行OpenCL编程时需要首先确定所选择的平台。

查询平台ID可以使用:

clGetPlatFormIDs(cl_uint num_entries, cl_platform_id*platforms, cl_uint *num_platforms)

       该函数的第一个参数是platforms可以保存的平台ID的数目;
                     第二个参数是一个指针,用来保存找到的平台;
                     第三个参数保存查询到的平台的个数。
       当返回值为0时,标志程序执行完成;非0时,为错误码。

       通常可以使用两次该函数来获取所有的平台ID:

cl_int ret;               //用于保存函数返回值
cl_uint num_platform;     //用于保存平台个数
cl_platform_id *platform  //用于保存平台ID

//////////////////////////////////////////////////////////////////////////
//获取函数个数
ret = clGetPlatformIDs(NULL, NULL, &num_platform);

//////////////////////////////////////////////////////////////////////////
//为platform分配空间
platform = new cl_platform_id[num_platform];

//获取所有平台
ret = clGetPlatformIDs(num_platform, platform, NULL);

查询平台信息可以使用:

cl_int clGetDeviceIDs(cl_platform_id platform,
                                      cl_platform_info param_name,
                                      size_t param_value_size,
                                      void *param_value,
                                      size_t *param_value_size_ret)

        该函数的第一个参数是之前获取的平台ID;
                      第二个参数是一个枚举体,用来标识所要查询的信息;
                      第三个参数是一个指针,用来存放查询到的信息;
                      第四个参数表示指针所指内存的大小;
                      第五个参数用来存放查询到的信息数据的大小。

       同样,通常使用两次该函数来获取平台信息:

cl_int ret;                   //用于保存函数返回值
size_t ext_size;       //用于保存平台信息大小
char *ext_data;      //用于保存平台信息
cl_platform_info platform_info = CL_PLATFORM_NAME;   //设置所要查询的信息

//////////////////////////////////////////////////////////////////////////
//获取平台信息大小
ret = clGetPlatformInfo(platform[order], platform_info, 0, NULL, &ext_size);

//////////////////////////////////////////////////////////////////////////
//为ext_data分配空间
ext_data = new char[ext_size];

//获取平台信息
ret = clGetPlatformInfo(platform[order], platform_info, ext_size, ext_data, NULL);

//打印平台信息
std::cout << ext_data << std::endl;


二、获取设备(Device):

       设备是运行在平台上的可进行异构计算的单元,通常对应于同一平台的处理器。获取设备ID和信息的方法与函数同获取平台类似:

cl_int clGetDeviceIDs(cl_platform_id platform,
                                      cl_device_type device_type,
                                      cl_uint num_entries,
                                      cl_device_id *devices,
                                      cl_uint *num_devices)

       第一个参数标识所要查询的平台,第二个参数用来保存查询到设备的类型,其余三个参数的含义与clGetPlatformIDs一致。

cl_int clGetDeviceInfo(cl_device_id device,
                                      cl_device_info param_name,
                                      size_t param_value_size,
                                      void *param_value,
                                      size_t *param_value_size_ret)

       第一个参数标识所要查询设备,其余参数的含义与clGetPlatformInfo一致。

       设备查询函数的使用和平台一样,一般通过两次调用来获得设备的个数和相应的信息,具体可以参考获取平台相关函数的使用方法。


三、建立上下文(Context):

       上下文(Context)是将同一平台上不同架构的设备连接在一起的纽带,在同一个上下文的设备才可以进行通信、读写、共享内存等操作。程序运行时,使用上下文来管理命令队列、内存、内核等对象。建立上下文可以使用:

cl_context clCreateContext(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)

       该函数第一个参数指向一个列表,用来表明建立的上下文的属性及相对应的值;
                  第二个参数表明上下文中设备的个数;
                  第三个参数是指向设备ID的指针,用来标明建立上下文的设备;
                  第四个参数是一个回调函数,报告建立上下文中出现的错误;
                  第五个参数用来返回错误码,当上下文建立成功时返回0,即CL_SUCCESS。
       如果建立上下文完成,该函数返回一个非0的上下文。

       实际使用时根据之前获得的设备ID来建立上下文:

cl_int ret;                   //用于保存函数返回值
cl_context_properties context_props[] 
= { CL_CONTEXT_PLATFORM, (cl_context_properties)platform[1], 0 };;   //设置上下文属性

//////////////////////////////////////////////////////////////////////////
//建立上下文
cl_context context = clCreateContext(context_props, num_device, devices, NULL, NULL, &ret);

       建立完上下文,就可以在其后进行建立命令队列、设置内核等操作。我们的OpenCL编程的万里长征也算是完成了第一步。

阅读更多
个人分类: OpenCL
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭