OpenCL入门一:Intel核心显卡OpenCL环境搭建

本文在Windows 10 64位系统上搭建OpenCL开发环境,笔记本显卡的类型(使用英特尔® 驱动程序和支持助理查询):

NVIDIA GeForce 820M Intel® HD Graphics Family
Adapter Compatibility NVIDIA Intel Corporation
Video Processor GeForce 820M Intel® HD Graphics Family
Driver Provider NVIDIA Intel Corporation
Driver Version 23.21.13.8857 20.19.15.4549
Driver Date 2017/12/4 2016/11/10
Adapter DAC Type Integrated RAMDAC Internal
Adapter RAM 2.00 GB 1.00 GB
Availability Offline Running at full power
Location PCI bus 4, device 0, function 0 PCI bus 0, device 2, function 0
Device Id PCI\VEN_10DE&DEV_1140&SUBSYS_228A1043&REV_A1\4&3A7A1238&0&00E4 PCI\VEN_8086&DEV_0A16&SUBSYS_228A1043&REV_09\3&11583659&0&10

以及Intel® HD Graphics Family的一些参数Resolution:1366 x 768, Bits Per Pixel:32,Number of Colors:4294967296,Refresh Rate - Current、 Refresh Rate - Maximum、Refresh Rate - Minimum:60 Hz。

准备工作

Windows 驱动中自动包含了OpenCL驱动,VS2012-VS2017任意版本(这里使用的是VS2017)。下载Intel SDK for OpenCL applications,注意选择Windows平台,然后注册帐号后即可下载。下载测试程序,解压后打开CapsBasic目录下的sln文件(高版本自动升级项目)。
在这里插入图片描述

测试

  1. 安装正确的话VS工具栏会有如下菜单:
    在这里插入图片描述
  2. 点击[生成]-[生成解决方案],不会有错误出现。在这里插入图片描述
  3. 运行结果在这里插入图片描述

新建项目

在这里插入图片描述
添加一个新文件HelloOpenCL.cpp,在文件中添加如下代码:

#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <cassert>
#include <CL/cl.h>

#define OCLBASIC_PRINT_TEXT_PROPERTY(NAME)                                \
{    /* 获得属性信息长度 */                                               \
 size_t property_length = 0;                                           \
 err = clGetDeviceInfo(device,NAME,0,0,&property_length);              \
 char* property_value = new char[property_length];                     \
 /* 获得属性信息 */                                                    \
 err = clGetDeviceInfo(device,NAME,property_length,property_value,0);  \
 std::cout << " " << #NAME << ": " << property_value << std::endl; \
 delete[] property_value;                                              \
}

int main()
{
// Discover and initialize the platforms
 cl_int err = CL_SUCCESS;
 cl_uint num_of_platforms = 0;
  // get total number of available platforms 获得可用平台总数
 err = clGetPlatformIDs(0, NULL, &num_of_platforms);
 std::cout << "Number of available platforms: " << num_of_platforms << std::endl;

 cl_platform_id* platforms = new cl_platform_id[num_of_platforms];
  // get IDs for all platforms
 err = clGetPlatformIDs(num_of_platforms, platforms, 0);

// List all platforms
for (cl_uint i = 0; i < num_of_platforms; ++i)
{
// Get the length for the i-th platform name 获得平台名长度
  size_t platform_name_length = 0;
  err = clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME, 0, 0, &platform_name_length);

// Get the name itself for the i-th platform 获得平台名字
  char* platform_name = new char[platform_name_length]; //为存储平台名分配空间
  err = clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME, platform_name_length, platform_name, 0);

// Output platform name
std::cout << " [" << i << "] " << platform_name << std::endl;
delete[] platform_name;
}

cl_platform_id platform = platforms[1]; // 选择Intel核显
// Discover the number of devices which are provided for the selected platform.
struct {
  cl_device_type type;
  const char* name;
  cl_uint count;
 } devices[] =
 {
  { CL_DEVICE_TYPE_CPU, "CL_DEVICE_TYPE_CPU", 0 },
  { CL_DEVICE_TYPE_GPU, "CL_DEVICE_TYPE_GPU", 0 },
  { CL_DEVICE_TYPE_ACCELERATOR, "CL_DEVICE_TYPE_ACCELERATOR", 0 }
 };
 const int NUM_OF_DEVICE_TYPES = sizeof(devices) / sizeof(devices[0]);
 std::cout << "Number of devices available for each type in selected platform:\n";
 // iterate over all device types 获得每种设备的数量
 for (int i = 0; i < NUM_OF_DEVICE_TYPES; ++i)
 {
 err = clGetDeviceIDs(platform, devices[i].type, 0, 0, &devices[i].count);
 if (CL_DEVICE_NOT_FOUND == err)
 {
   devices[i].count = 0;
   err = CL_SUCCESS;
 }
 std::cout << " " << devices[i].name << ": " << devices[i].count << std::endl;
 }

// get useful capabilities information for each device
std::cout << "\n*** Detailed information for each device ***\n";
for (int type_index = 0; type_index < NUM_OF_DEVICE_TYPES; ++type_index)
{
cl_uint cur_num_of_devices = devices[type_index].count;
if (cur_num_of_devices == 0)
{
continue; // there is no devices of this type; move to the next type
}
// Retrieve a list of device IDs with type selected by type_index 检索设备ID列表
cl_device_id* devices_of_type = new cl_device_id[cur_num_of_devices];
err = clGetDeviceIDs(platform,devices[type_index].type,cur_num_of_devices,devices_of_type,0);

// Iterate over all devices of the current device type
for (cl_uint device_index = 0; device_index < cur_num_of_devices; ++device_index)
{
std::cout << "\n" << devices[type_index].name << "[" << device_index << "]\n";
cl_device_id device = devices_of_type[device_index];

#define OCLBASIC_PRINT_NUMERIC_PROPERTY(NAME, TYPE)              \
            {                                                    \
            TYPE property_value;                                 \
            size_t property_length = 0;                          \
            err = clGetDeviceInfo(                               \
            device,                                              \
            NAME,                                                \
            sizeof(property_value),                              \
            &property_value,                                     \
            &property_length                                     \
            );                                                   \
            assert(property_length == sizeof(property_value));   \
            std::cout                                            \
            << " " << #NAME << ": "                           \
            << property_value << std::endl;                      \
            }

   OCLBASIC_PRINT_TEXT_PROPERTY(CL_DEVICE_NAME);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_AVAILABLE, cl_bool);
   OCLBASIC_PRINT_TEXT_PROPERTY(CL_DEVICE_VENDOR);
   OCLBASIC_PRINT_TEXT_PROPERTY(CL_DEVICE_PROFILE);
   OCLBASIC_PRINT_TEXT_PROPERTY(CL_DEVICE_VERSION);
   OCLBASIC_PRINT_TEXT_PROPERTY(CL_DRIVER_VERSION);
   OCLBASIC_PRINT_TEXT_PROPERTY(CL_DEVICE_OPENCL_C_VERSION);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_MAX_COMPUTE_UNITS, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_MAX_CLOCK_FREQUENCY, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_MAX_WORK_GROUP_SIZE, size_t);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_ADDRESS_BITS, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_MEM_BASE_ADDR_ALIGN, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_MAX_MEM_ALLOC_SIZE, cl_ulong);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_GLOBAL_MEM_SIZE, cl_ulong);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, cl_ulong);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, cl_ulong);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_LOCAL_MEM_SIZE, cl_ulong);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_PROFILING_TIMER_RESOLUTION, size_t);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_IMAGE_SUPPORT, cl_bool);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_ERROR_CORRECTION_SUPPORT, cl_bool);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_HOST_UNIFIED_MEMORY, cl_bool);
OCLBASIC_PRINT_TEXT_PROPERTY(CL_DEVICE_EXTENSIONS);
OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_NATIVE_VECTOR_WIDTH_INT, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT, cl_uint);
   OCLBASIC_PRINT_NUMERIC_PROPERTY(CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE, cl_uint);
}
delete[] devices_of_type;
}
delete[] platforms;
return 0;
}

API函数clGetPlatformIDs()用来获取指定系统上可用的计算平台:

cl_int clGetPlatformIDs(cl_uint num_entries, cl_platform_id *platforms, cl_uint *num_platforms)

该函数通常由应用程序调用两次。首次调用时,将cl_uint指针和NULL传递给num_platforms和platforms参数。num_platforms即为可用平台的数量。第二次调用,将num_entries平台分配足够空间夫人地址传递给函数的cl_platform_id指针。

clGetPlatformInfo

API函数clGetDeviceIDs()获取指定平台上设备的数量。

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

从示例代码中看出cl_device_type的类型有:CL_DEVICE_TYPE_CPU、CL_DEVICE_TYPE_GPU、CL_DEVICE_TYPE_ACCELERATOR。

三个平台:NVIDIA CUDA、Intel® OpenCL、Experimental OpenCL 2.1 CPU Only Platform
对于Intel® OpenCL的详细设备情况:有一个CPU和一个GPU。
在这里插入图片描述
在这里插入图片描述
对于NVIDIA CUDA的详细设备情况:一个GPU。
在这里插入图片描述

对于Experimental OpenCL 2.1 CPU Only Platform的详细设备情况:只有一个CPU
在这里插入图片描述

对测试工程的分析

错误处理:

#define CAPSBASIC_CHECK_ERRORS(ERR)        \
    if(ERR != CL_SUCCESS)                  \
    {                                      \
    cerr                                   \
    << "OpenCL error with code " << ERR    \
    << " happened in file " << __FILE__    \
    << " at line " << __LINE__             \
    << ". Exiting...\n";                   \
    exit(1);                               \
    }

通过宏定义对错误代码,文件和行数进行输出,并退出。

#define OCLBASIC_PRINT_TEXT_PROPERTY(NAME)                       \
            {                                                    \
            /* When we query for string properties, first we */  \
            /* need to get string length:                    */  \
            size_t property_length = 0;                          \
            err = clGetDeviceInfo(                               \
            device,                                              \
            NAME,                                                \
            0,                                                   \
            0,                                                   \
            &property_length                                     \
            );                                                   \
            CAPSBASIC_CHECK_ERRORS(err);                         \
            /* Then allocate buffer. No need to add 1 symbol */  \
            /* to store terminating zero; OpenCL takes care  */  \
            /* about it:                                     */  \
            char* property_value = new char[property_length];    \
            err = clGetDeviceInfo(                               \
            device,                                              \
            NAME,                                                \
            property_length,                                     \
            property_value,                                      \
            0                                                    \
            );                                                   \
            CAPSBASIC_CHECK_ERRORS(err);                         \
            cout                                                 \
            << "    " << #NAME << ": "                           \
            << property_value << endl;                           \
            delete [] property_value;                            \
            }

使用宏函数:

  1. 针对宏定义中换行,换行符\必不可少(换行符加回车键)。针对执行语句的换行,我们同样可以使用\作为换行符,同时当一个语句过长时,直接换行,也并不会影响语句的编译。
  2. #NAME
    在这里插入图片描述
    这里的#NAME是使用NAME宏参数的字符串值来取代,从而打印出参数。由上图的示例可以看出。
  3. 在上面的测试代码中,将该段宏定义写在了主函数前面,有在main函数定义的err,并不是全局变量,但是并没有报错。说明编译器并不对宏定义进行语法检查。
#define OCLBASIC_PRINT_NUMERIC_PROPERTY(NAME, TYPE)              \
            {                                                    \
            TYPE property_value;                                 \
            size_t property_length = 0;                          \
            err = clGetDeviceInfo(                               \
            device,                                              \
            NAME,                                                \
            sizeof(property_value),                              \
            &property_value,                                     \
            &property_length                                     \
            );                                                   \
            assert(property_length == sizeof(property_value));   \
            CAPSBASIC_CHECK_ERRORS(err);                         \
            cout                                                 \
            << "    " << #NAME << ": "                           \
            << property_value << endl;                           \
            }

这里输入的TYPE是变量的类型,用于在宏定义代码中申明代码。

猜你喜欢

转载自blog.csdn.net/asmartkiller/article/details/86095773