向量加法
程序流程
创建平台
//创建平台对象
status = clGetPlatformIDs( 1, &platform, NULL );
注意:上式是选择默认的第一个平台。如果我们系统中安装不止一个opencl平台,如何选择自己需要的平台? 比如我现在安装了intel和NVIDIA平台。那么我们就需要进行一个选择判断。第一次调用是获取平台的数量,numPlatforms里面存的就是平台的数量。第二个是获取可用的平台。另外,我也没有增加错误检测之类的代码,但是我增加了一个 status 的变量,通常如果函数执行正确,返回的值是 0。
/*Step1: Getting platforms and choose an available one.*/
cl_uint numPlatforms; //the NO. of platforms
cl_platform_id platform = NULL; //the chosen platform
cl_int status = clGetPlatformIDs(0, NULL, &numPlatforms);
if (status != CL_SUCCESS)
{
cout << "Error: Getting platforms!" << endl;
return FAILURE;
}
/*For clarity, choose the first available platform. */
if(numPlatforms > 0)
{
cl_platform_id* platforms = (cl_platform_id* )malloc(numPlatforms* sizeof(cl_platform_id));
status = clGetPlatformIDs(numPlatforms, platforms, NULL);
platform = platforms[0];
free(platforms);
}
选择设备
询问设备名称,并选择一个。
/*Step 2:Query the platform and choose the first GPU device if has one.Otherwise use the CPU as device.*/
cl_uint numDevices = 0;
cl_device_id *devices;
status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, NULL, &numDevices);
if (numDevices == 0) //no GPU available.
{
cout << "No GPU device available." << endl;
cout << "Choose CPU as default device." << endl;
status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_CPU, 0, NULL, &numDevices);
devices = (cl_device_id*)malloc(numDevices * sizeof(cl_device_id));
status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_CPU, numDevices, devices, NULL);
}
else
{
devices = (cl_device_id*)malloc(numDevices * sizeof(cl_device_id));
status = clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, numDevices, devices, NULL);
}
创建上下文
下面我们来看下 OpenCL 中 Context 的概念。通常,Context 是指管理 OpenCL 对象和资源的上下文环境。为了管理 OpenCL 程序,下面的一些对象都要和 Context 关联起来:
• 设备(Devices): 执行 Kernel 程序对象。
• 程序对象(Program objects): kernel 程序源代码
• Kernels: 运行在 OpenCL 设备上的函数
• 内存对象(Memory objects): 设备上存放数据
• 命令队列(Command queues): 设备的交互机制
• 内存命令(Memory commands)(用于在主机内存和设备内存之间拷贝数据)
• Kernel 执行(Kernel execution)
• 同步(Synchronization)
注意:创建一个 Context 的时候,我们必须把一个或多个设备和它关联起来。对于其它的 OpenCL 资源,它们创建时候,也要和 Context 关联起来,一般创建这些资源的 OpenCL 函数的输入参数中,都会有 context。
/*Step 3: Create context.*/
cl_context context = clCreateContext(NULL,1, devices,NULL,NULL,NULL);
创建命令队列
接下来,我们要看下命令队列。在 OpenCL 中,命令队列就是主机的请求,在设备上执行的一种机制。
在 Kernel 执行前,我们一般要进行一些内存拷贝的工作,比如把主机内存中的数据传输到设备内存中。
另外要注意的几点就是:对于不同的设备,它们都有自己的独立的命令队列;命令队列中的命令 (kernel 函数)可能是同步的,也可能是异步的,它们的执行顺序可以是有序的,也可以是乱序的。
命令队列在 device 和 context 之间建立了一个连接。
命令队列 properties 指定一下内容:
• 是否乱序执行(在 AMD GPU 中,好像现在还不支持乱序执行)
• 是否启动 profiling。 Profiling 通过事件机制来得到 kernel 执行时间等有用的信息,但它本身也会有一些开销。
/*Step 4: Creating command queue associate with the context.*/
cl_command_queue commandQueue = clCreateCommandQueue(context, devices[0], 0, NULL);
创建程序对象
clCreateProgramWithSource()这个函数通过源代码 (strings),创建一个程序对象,其中 counts 指定源代码串的数量,lengths 指定源代码串的长度(为 NULL 结束的串时,可以省略)。当然,我们还必须自己编写一个从文件中读取源代码串的函数。
/*Step 5: Create program object */
const char *filename = "Vadd.cl";
string sourceStr;
status = convertToString(filename, sourceStr);
const char *source = sourceStr.c_str();
size_t sourceSize[] = {strlen(source)};
cl_program program = clCreateProgramWithSource(context, 1, &source, sourceSize, NULL);
//从文件中读取源代码串的函数
/* convert the kernel file into a string */
int convertToString(const char *filename, std::string& s)
{
size_t size;
char* str;
std::fstream f(filename, (std::fstream::in | std::fstream::binary));
if(f.is_open())
{
size_t fileSize;
f.seekg(0, std::fstream::end);
size = fileSize = (size_t)f.tellg();
f.seekg(0, std::fstream::beg);
str = new char[size+1];
if(!str)
{
f.close();
return 0;
}
f.read(str, fileSize);
f.close();
str[size] = '\0';
s = str;
delete[] str;
return 0;
}
cout</