OpenCL设置 clCreateBuffer 属性对整体性能的影响

OpenCL设置 clCreateBuffer 属性对整体性能的影响

一,函数介绍

这里讨论clCreateBuffer 对性能的影响,就需要先对OpenCL的clCreateBuffer函数进行介绍了。

cl_mem clCreateBuffer(cl_context context,
    				  cl_mem_flags flags,
    				  size_t size,
                      void* host_ptr,
                      cl_int* errcode_ret
);

具体每个参数的作用这里就不着重分析了,主要来看第二个参数 flags , 的设置对计算机CPU性能的影响。

cl_mem_flagsDescription
CL_​MEM_​READ_​WRITE设备可以读写,host_ptr可为空,这表明该参数不需要初始化
CL_​MEM_​WRITE_​ONLY设备只写,host_ptr可为空,表明不需要初始化
CL_​MEM_​READ_​ONLY设备只读,不能为空
CL_​MEM_​USE_​HOST_​PTR
CL_​MEM_​ALLOC_​HOST_​PTR
CL_​MEM_​COPY_​HOST_​PTR

CL_MEM_USE_HOST_PTR:这个只有在host_ptr 不为NULL的情况下才是有效的。当它指明时,OpenCL的实现将使用由host_ptr 指向的内存来作为cl_mem对象的存储,将host_ptr 指向的内容缓冲(cache)到对应的设备上,在kernel执行的过程中就可以使用这些内容。
如果同一段内存来创建多个buffer(例如同一个host_ptr 创建多个buffer,或者多个buffer使用的指针在内存中有重叠的部分),OpenCL指令对这些buffer的操作结果是未定义的。

CL_MEM_ALLOC_HOST_PTR:这个选项要求OpenCL在主程序可以访问的存储位置来放置buffer。它和CL_MEM_USE_HOST_PTR是互斥的。

CL_MEM_COPY_HOST_PTR:这个只有在host_ptr 不为NULL的情况下才是有效的。它要求OpenCL为存储对象分配空间并复制(copy)host_ptr 指向的内容到相应的存储中。它和CL_MEM_USE_HOST_PTR是互斥的。
它和CL_MEM_ALLOC_HOST_PTR同时使用时,指明:将cl_mem分配在主程序可访问的存储位置(例如,PCIe),并进行了初始化。

以上对函数的属性设置大致就是这样,接下来,我会讲解我之前是怎样做的,遇到了什么问题,后来是如何修改的,结果如何。

二、问题背景

最近在研究如何对yolov3 中forward部分进行opencl加速,写了一个opencl进行数据转换和做exp运算的kernel,实际运算效果感人,毕竟是并行运算。
业务场景是这样的:传递一个大数组到GPU,运算后的结果分别保存至两个数组。
但是调用了clCreateBuffer,缺占用了很大一部分CPU耗时。结果如下:

  
 	/* 創建内存對象 */
	controller->memdata[0] = clCreateBuffer(controller->context, CL_MEM_READ_ONLY |CL_MEM_COPY_HOST_PTR, datasize * sizeof(char), input, &controller->err);
	controller->memdata[1] = clCreateBuffer(controller->context, CL_MEM_READ_WRITE |CL_MEM_COPY_HOST_PTR, datasize * sizeof(float), mid_data, &controller->err);
	controller->memdata[2] = clCreateBuffer(controller->context, CL_MEM_READ_WRITE |CL_MEM_COPY_HOST_PTR, datasize * sizeof(float), output, &controller->err);

在这里插入图片描述
可以看到, 当我把每个BUF的属性都设置为CL_MEM_COPY_HOST_PTR,按着官方文档的解释,设备会将主存里的数据copy到显存里,所里问题就明显了, 我后面的两个buf的数据明显不需要copy, 只是作为一个缓存用来暂时存放结果数据的,取到数据后就会释放掉。所以,问题就很明显了,代码修改为:

	controller->memdata[0] = clCreateBuffer(controller->context, CL_MEM_READ_ONLY |CL_MEM_COPY_HOST_PTR, datasize * sizeof(char), input, &controller->err);
	controller->memdata[1] = clCreateBuffer(controller->context, CL_MEM_READ_WRITE, datasize * sizeof(float), NULL, &controller->err);
	controller->memdata[2] = clCreateBuffer(controller->context, CL_MEM_READ_WRITE, datasize * sizeof(float), NULL, &controller->err);

在这里插入图片描述
可以看到,clCreateBuffer的时间明显降低了不少。

三、总结

在创建内存对象的时候,要考虑一下对象内存的用途,如果是用来做数据传输的,将主存数据传输给显存数据,那么一般要设置CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR属性, 如果内存对象是用来保存结果数据的,那么内存属性只需要设置CL_MEM_READ_WRITE属性。结果数据则需要在主机端通过:clEnqueueReadBuffer函数读取。

另外,我发现 CL_MEM_USE_HOST_PTR属性的解释为:应用程序希望OpenCL实现使用host_ptr引用的内存作为内存对象的存储位。OpenCL实现允许在设备内存中缓存host_ptr所指向的缓冲区内容。当在设备上执行内核时,可以使用这个缓存副本。
按着我的理解,是不是使用了这个属性,数据的修改便是同步的,主机端便不需要内存的读取了?我先去做点实验,下篇文章和大家讨论。


版权声明:本文为qq_38505858原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。