除了可使用读/写函数在主机与设备间拷贝数据之外,OpenCL还提供了把设备上的存储器对象映射到主机可访问的内存区域。一旦映射确定后,可以在主机端通过指针读取或修改设备存储器对象的值。对于缓冲区对象和图像对象都可以映射。
缓冲区对象映射
可以使用如下函数来映射缓冲区对象到主机内存区域:
void *clEnqueueMapBuffer (cl_command_queue command_queue,
cl_mem buffer,
cl_bool blocking_map,
cl_map_flags map_flags,
size_t offset,
size_t size,
cl_uint num_events_in_wait_list,
const cl_event *event_wait_list,
cl_event *event,
cl_int *errcode_ret)
- 参数command_queue为一个有效的主机命令队列。
-
参数blocking_map表明此映射是阻塞的还是非阻塞的。如果blocking_map为CL_TRUE,即表示映射命令是阻塞的,直到映射完成,函数才会返回。应用可以用返回的指针访问所映射区域的内容;如果blocking_map为CL_FALSE,即映射为非阻塞的,直到映射命令完成后才能使用返回的指针。参数event会返回一个事件对象,可用来查询映射命令的执行情况。在完成映射后,应用才可以使用返回的指针访问映射区域的内容。
-
参数map_f lags为位域,用于描述映射区域的状态。详细见下表。
-
参数offset和size为所要映射的区域在缓冲区对象中的偏移量和大小,单位为字节。其余参数与5.4.1节中同名参数意义一样,在此就不赘述。
函数clEnqueueMapBuffer实现把缓冲区对象映射到主机可访问的内存区域。//创建上下文和命令队列
buffer = clCreateBuffer(context,
CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR,
sizseof(float) * NUM_ELEMENTS, &err);
cl_float *inputData = (cl_float *)clEnqueueMapBuffer(cmdqueue,
buffer, CL_TRUE,
CL_MAP_WRTTE, 0, sizeof(float) * NUM_ELEMENTS,
0, NULL, NULL,
&err);
//对inputData写入操作
err = clEnqueueUnmapMemObject(cmdqueue, buffer, inputData, 0,
NULL, NULL);
//执行内核
cl_float *outData = (cl_float *)clEnqueueMapBuffer(cmdqueue,
buffer, CL_TRUE,
CL_MAP_REA, 0, sizeof(float) * NUM_ELEMENTS, 0,
NULL, NULL,
&err);
//对outData操作
err = clEnqueueUnmapMemObject(cmdqueue, buffer, outData, 0, NULL,
NULL);
在例子中,我们使用CL_MEM_READ_WRITE|CL_MEM_ALLOC_HOST_PTR属性来分配缓冲区对象。把分配好的缓冲区对象通过map()/unmap()来映射或解映射到主机,避免了数据在主机与设备间来回拷贝。分配示意图如下图所示。
OpenCL默认假设主机与GPU的内存是各自独立的,分配缓冲区将会牵涉数据拷贝问题。对于AMD APU设备或者ARM Mali设备,主机与GPU共享内存,如果采用在主机与设备上分配缓冲区,这显然是浪费时间和功耗的。我们可以使用如下代码来避免数据拷贝:
#define NUM_ELEMENTS 1000
cl_int err;
cl_context context;
cl_command_queue cmdqueue;
cl_mem buffer;