文章

OpenCL Buffer Objects

1. clCreateBuffer 分配内存

创建 OpenCL 内存对象函数原型为:

1
2
3
4
5
clCreateBuffer(cl_context,   // 上下文
              cl_mem_flags,  // 内存对象的性质,见下表
              size_t,        // 内存对象数据块大小
              void *,        // host_ptr 主机数据内存地址(可以为空)
              cl_int *);

针对不同场景需求,OpenCL提供了不同的内存对象创建标志位。

1.1. CL_MEM_USE_HOST_PTR

1
2
3
4
auto src_matrix_ptr = aligned_malloc<int, 4096>(kMatrixSize * kMatrixSize);
// fill the matrix with data...

cl_mem clsrc = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, cl_buff_size, src_matrix_ptr, NULL);

由于是 host malloc 分配的内存,runtime 会分配一个相应的 bufferkernel开始执行时,将从host_ptr拷贝到OpenCLbuffer中。应该避免使用该标志位

所谓开始执行时,是指NDRangeKernel创建的命令,在device上执行时,状态变为Ready的时候。

针对与host共享物理内存的device,如果host_ptr已经是地址对齐的,那么runtime应该不用分配内存了。如果host_ptr没有内存对齐,则runtime将进行拷贝操作。(要考虑物理内存页要连续??)。

1
2
Use this when an aligned buffer already exists on the host side.  It must be aligned to a 4096 byte boundary and be a multiple of 64 bytes or you don't actually get zero copy.
 Below clCreateBuffer takes a host address and returns a corresponding device address.

CL_MEM_USE_HOST_PTR

1.2. CL_MEM_ALLOC_HOST_PTR

1
2
3
cl_mem clsrc = clCreateBuffer(context, CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR, cl_buff_size, NULL, NULL);
int* map_cl_src = (int*)clEnqueueMapBuffer(queue, clsrc, CL_TRUE, CL_MAP_WRITE, 0, cl_buff_size, 0, NULL, NULL, &status);
memcpy(map_cl_src, src_matrix_ptr, cl_buff_size);
在`device`内存空间分配`buffer`。

通过使用clEnqueueMapBuffer提供对host的访问入口。这是最佳的内存分配方式,避免runtime内存拷贝操作。

CL_MEM_ALLOC_HOST_PTR

1.3. CL_MEM_ALLOC_HOST_PTR | CL_MEM_COPY_HOST_PTR

添加 CL_MEM_COPY_HOST_PTR 标志位,runtime 会将 host_ptr 的数据拷贝到 OpenCLbuffer 中。适用于`host_ptr`没有内存地址对齐

1.4. 场景选择

  • 如果可能,尽量使用 CL_MEM_ALLOC_HOST_PTR 标志位,避免runtime内存拷贝操作。
  • 如果host_ptr已经是地址对齐的,可以使用 CL_MEM_USE_HOST_PTR 标志位。
  • 如果host_ptr没有内存地址对齐,可以使用 CL_MEM_ALLOC_HOST_PTR | CL_MEM_COPY_HOST_PTR 标志位。

1.5. 参考资料

本文由作者按照 CC BY 4.0 进行授权