OpenCL 分配缓冲区对象

存储器对象是OpenCL的一个基本概念,而缓冲区对象是存储器对象实体。缓冲区对象是1维的内存资源,可以包含标量、矢量或用户自定义的数据类型。可以使用如下函数来分配缓冲区对象:

cl_mem clCreateBuffer ( cl_context context,
                              cl_mem_flags flags,
                              size_t size,
                              void *host_ptr,
                              cl_int *errcode_ret)
  • 参数context是一个合法的上下文对象,要为这个上下文分配缓冲区对象。
  • 参数size是所分配缓冲区的大小,用字节表示。
  • 参数host_ptr指向可能已经由应用分配好的缓冲数据,大小必须〉=size,这个参数如何使用由参数f lags确定。
  • 参数errcode_ret用来返回错误码。
  • 参数f lags是一个位域,用来指定缓冲区的分配和使用信息,可取的值如下表所示。

cl_mem_flags值

CL_MEM_READ_ONLY、CL_MEM_WRITE_ONLY和CL_MEM_READ_WRITE用于描述存储器对象在内核中的访问属性。
CL_MEM_ALLOC_HOST_PTR、CL_MEM_COPY_HOST_PTR和CL_MEM_USE_HOST_PTR用于描述主机指针标志。这三个标志在笔者初次接触OpenCL编程时也比较迷糊,在此笔者就具体讲述这三个标志值的区别:

  1. CL_MEM_ALLOC_HOST_PTR:在主机上分配存储器对象,并且主机端可以访问这些存储器对象。在OpenCL存储模型中,如果主机与设备内存是分开的,那么在缓冲区分配时将涉及内存拷贝。但是在ARM Mali、Intel Ivy Bridge架构之后的处理器和AMD APU中, GPU与主机共享存储的平台,主机和设备之间共享内存,无须在主机与设备间数据来回拷贝。使用clEnqueueMapBuffer函数,把分配的存储器对象映射到主机端,主机直接操作存储器
    对象;主机完成操作以后,使用clEnqueueUnmapMemObject函数来取消存储器对象的映射。如代码:
1.cl_mem memobject = clCreateBuffer(CL_MEM_READ_ONLY |
                                          CL_MEM_ALLOC_HOST_PTR);
2.address = clMapBuffer(buffer);
3.memset(address)或者memcpy(address);
4.clEnqueueUnmapMemObject(buffer);
  1. CL_MEM_COPY_HOST_PTR:在设备上分配存储器对象,并且使用host_ptr指向的主机地址空间的值来初始化存储器对象。在使用CL_MEM_COPY_HOST_PTR标志时,参数host_ptr不能为NULL。如下例子:
int host_data[1000];
for(int i = 0; i 〈 1000; i++)
    host_data[i] = i;
cl_mem memobjct = clCreateBuffer(context,
                                        CL_MEM_READ_WRITE |
                                        CL_MEM_COPY_HOST_PTR,
                                        sizeof(int) * 1000, host_data,
                                        NULL);

在上述例子中,存储器对象memobject初始值为0,1,2,3,…,999。

  1. CL_MEM_USE_HOST_PTR:直接使用主机端上已经分配好的内存空间给设备使用,内核在设备上执行时将host_ptr指向的内容缓冲到对应的设备上。在使用CL_MEM_USE_HOST_PTR时,host_ptr不能为NULL。需要注意的是,虽然使用了主机端已存在的内存,但是创建的存储器对象值不一定和原来的主机内存相同,这依赖于不同OpenCL的实现方式。如下代码:
float data[1000];
float *output;
for(int i = 0; i 〈 N; i++)
    data[i] = 1;
cl_mem bufferA = clCreateBuffer(context,
                                      CL_MEM_READ_ONLY |
                                      CL_MEM_USE_HOST_PTR,
                                      sizeof(float) * N, data, &err);
…//执行内核
output = (cl_float *)clEnqueueMapBuffer(cmdqueue, bufferA,
                                                CL_TRUE,
                                                CL_MAP_WRTTE, 0,
                                                sizeof(float) * 1000,
                                                0, NULL, NULL, &err);
...

以上代码,在笔者AMD A10-6400P的GPU执行后,data和output数组中的值是一致的。

CL_MEM_HOST_WRITE_ONLY、CL_MEM_HOST_READ_ONLY和CL_MEM_HOST_NO_ACCESS用于描述主机访问属性。对于存储器对象,除了内核函数的访问,其他的访问基本都是主机访问,如clEnqueueRead/WriteBuffer。在存储器对象分配时设置主机访问属性后,如果随后的主机代码对存储器对象的访问属性与分配时设定主机访问属性不同,则有运行时错误。例子如下:

float src[1000];
for(int i = 0; i 〈 1000; i++)
    src[i] = i;
cl_mem mem = clCreateBuffer(context,
                                  CL_MEM_HOST_READ_ONLY |
                                  CL_MEM_READ_WRITE,
                                  sizeof(float) * 1000, NULL, &err);
clEnqueueWriteBuffer(cmdQueue0, mem, CL_TRUE, 0,
sizeof(float) * 1000, src, 0, NULL, NULL);

以上代码在创建缓冲时设置主机只能读,而接下来的代码却对缓冲进行写操作。在运行程序时,则有CL_INVALID_OPERATION错误。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程