2

我对 OpenCL 还很陌生,虽然到目前为止我已经理解了所有内容,但是我无法理解缓冲区对象的工作原理。

我不明白缓冲区对象的存储位置。在这个StackOverflow 问题中指出:

如果您只有一台设备,可能 (99.99%) 将在设备中。(在极少数情况下,如果设备暂时没有足够的内存,它可能在主机中)

对我来说,这意味着缓冲区对象存储在设备内存中。但是,正如StackOverflow 问题中所述,如果在 中使用该标志,CL_MEM_ALLOC_HOST_PTR使用clCreateBuffer的内存很可能是固定内存。我的理解是,当内存被固定时,它不会被换出。这意味着固定内存必须位于 RAM 中,而不是设备内存中。

那么实际发生了什么?

我想知道标志是什么:

  • CL_MEM_USE_HOST_PTR
  • CL_MEM_COPY_HOST_PTR
  • CL_MEM_ALLOC_HOST_PTR

暗示缓冲区的位置。

谢谢

4

2 回答 2

3

我们先来看看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. 运行时将为内部主机存储分配内存。它可以被钉住。

希望有帮助。

于 2018-11-26T12:45:49.913 回答
3

规范(故意?)在主题上含糊不清,给实现者留下了很大的自由。因此,除非您所针对的 OpenCL 实现对标志做出明确保证,否则您应该将它们视为建议。

首先,CL_MEM_COPY_HOST_PTR实际上与分配无关,它只是意味着您想用传递给调用clCreateBuffer的内存内容预先填充分配的内存。host_ptr这就像您clCreateBuffer在使用host_ptr = NULL和不使用此标志的情况下调用,然后进行阻塞clEnqueueWriteBuffer调用以写入整个缓冲区。

关于分配方式:

  • CL_MEM_USE_HOST_PTR- 这意味着您已经预先分配了一些内存,正确对齐,并希望将其用作缓冲区的后备内存。如果设备不支持直接访问主机内存,或者如果驱动程序决定将卷影复制到 VRAM 将比直接访问系统更有效,则该实现仍然可以分配设备内存并在缓冲区和分配的内存之间来回复制记忆。但是,在可以直接从系统内存读取的实现中,这是零拷贝缓冲区的一种选择。
  • CL_MEM_ALLOC_HOST_PTR- 这是一个提示,告诉 OpenCL 实现您计划通过将缓冲区映射到主机地址空间来从主机端访问缓冲区,但与 不同CL_MEM_USE_HOST_PTR的是,您将分配本身留给 OpenCL 实现。对于支持它的实现,这是零复制缓冲区的另一种选择:创建缓冲区,将其映射到主机,获取主机算法或 I/O 以写入映射的内存,然后取消映射并在 GPU 内核中使用它. 与 不同CL_MEM_USE_HOST_PTR的是,这为使用可直接映射到 CPU 地址空间(例如 PCIe BAR)的 VRAM 敞开了大门。
  • 默认(以上两种都不是):分配给设备最方便的地方。通常是 VRAM,如果设备不支持将内存映射到主机内存,这通常意味着如果将其映射到主机地址空间,最终会得到 2 个缓冲区副本,一个在 VRAM 中,一个在系统内存中,而 OpenCL 实现在 2 之间来回复制。

请注意,实现还可以使用提供的任何访问标志(CL_MEM_HOST_WRITE_ONLYCL_MEM_HOST_READ_ONLYCL_MEM_HOST_NO_ACCESSCL_MEM_WRITE_ONLYCL_MEM_READ_ONLYCL_MEM_READ_WRITE)来影响分配内存的决定。

最后,关于“固定”内存:许多现代系统都有一个 IOMMU,当它处于活动状态时,来自设备的系统内存访问可能会导致 IOMMU 页面错误,因此从技术上讲,主机内存甚至不需要常驻。在任何情况下,OpenCL 实现通常都与内核级设备驱动程序深度集成,该驱动程序通常可以按需固定系统内存范围(将它们从分页中排除)。因此,如果使用CL_MEM_USE_HOST_PTR,您只需要确保提供适当对齐的内存,并且实现将为您处理固定。

于 2018-11-26T12:56:28.820 回答