我们先来看看clCreateBuffer的签名:
cl_mem clCreateBuffer(
cl_context context,
cl_mem_flags flags,
size_t size,
void *host_ptr,
cl_int *errcode_ret)
这里没有任何参数可以为 OpenCL 运行时提供一个确切的设备,缓冲区应该被放置到其内存中,因为一个上下文可以有多个设备。运行时仅在我们使用缓冲区对象时才知道,例如从/向其读取/写入,因为这些操作需要连接到特定设备的命令队列。
每个内存对象都驻留在主机内存或上下文设备的内存之一中,运行时可能会根据需要迁移它。所以一般来说,每个内存对象都可能在 OpenCL 运行时中有一块内部主机内存。运行时实际上所做的是依赖于实现的,所以我们不能不做太多假设而得不到可移植的保证。这意味着关于 pinning 等的一切都是依赖于实现的,你只能希望最好,但避免使用肯定会阻止使用 pinned memory 的模式。
为什么我们需要固定内存?
固定内存意味着,我们进程地址空间中内存页面的虚拟地址具有固定转换为 RAM 的物理内存地址。这可以在 GPU 的设备内存和使用 PCIe 的 CPU 内存之间实现 DMA(直接内存访问)传输(对物理地址进行操作)。DMA 降低了 CPU 负载并可能提高了复制速度。所以我们希望我们的 OpenCL 内存对象的内部主机存储被固定,以提高内部主机存储和 OpenCL 内存对象的设备内存之间的数据传输性能。
作为一个基本的经验法则:如果您的运行时分配主机内存,它可能会被固定。如果你在你的应用程序代码中分配它,运行时会悲观地假设它没有被固定——这通常是一个正确的假设。
CL_MEM_USE_HOST_PTR
允许我们为 OpenCL 实现提供内存,用于对象的内部主机存储。如果我们调用内核,这并不意味着内存对象不会迁移到设备内存中。由于该内存是用户提供的,因此运行时不能假定它是固定的。这可能会导致在设备传输之前在未固定的内部主机存储和固定的缓冲区之间进行额外的复制,以便为主机设备传输启用 DMA。
CL_MEM_ALLOC_HOST_PTR
我们告诉运行时为对象分配主机内存。它可以被固定。
CL_MEM_COPY_HOST_PTR
我们提供主机内存来复制初始化我们的缓冲区,而不是在内部使用它。我们也可以将它与CL_MEM_ALLOC_HOST_PTR
. 运行时将为内部主机存储分配内存。它可以被钉住。
希望有帮助。