OpenCL 缓冲区创建
OpenCL Buffer Creation
我是 OpenCL 的新手,虽然到目前为止我已经了解了所有内容,但我在理解缓冲区对象的工作原理时遇到了困难。
我一直不明白缓冲区对象存储在哪里。在 this Whosebug 问题中指出:
If you have one device only, probably (99.99%) is going to be in the device. (In rare cases it may be in the host if the device does not have enough memory for the time being)
对我来说,这意味着缓冲区对象存储在设备内存中。但是,如 this Whosebug 问题中所述,如果在 clCreateBuffer
中使用标志 CL_MEM_ALLOC_HOST_PTR
,则使用的内存很可能是固定内存。我的理解是,当内存被固定时,它不会被换出。这意味着固定内存必须位于 RAM 中,而不是设备内存中。
那么实际发生了什么?
我想知道flags是做什么的:
CL_MEM_USE_HOST_PTR
CL_MEM_COPY_HOST_PTR
CL_MEM_ALLOC_HOST_PTR
暗示缓冲区的位置。
谢谢
先来看看clCreateBuffer的签名:
cl_mem clCreateBuffer(
cl_context context,
cl_mem_flags flags,
size_t size,
void *host_ptr,
cl_int *errcode_ret)
这里没有论点可以为 OpenCL 运行时提供缓冲区应放入其内存中的确切设备,因为上下文可以有多个设备。运行时仅在我们使用缓冲区对象时才知道,例如read/write from/to 它,因为这些操作需要一个连接到特定设备的命令队列。
每个内存对象都驻留在主机内存或上下文设备的内存之一中,并且运行时可能会根据需要迁移它。 所以一般来说,每个内存对象在 OpenCL 运行时可能有一块内部主机内存。运行时实际做的是依赖于实现的,所以我们不能不做太多假设并得到没有便携式保证。这意味着有关固定等的一切都取决于实现,您只能希望最好,但要避免肯定会阻止使用固定内存的模式。
为什么我们要固定内存?
固定内存意味着,我们进程地址 space 中内存页的虚拟地址具有固定转换为 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
结合起来。运行时将为内部主机存储分配内存。 可以固定。
希望对您有所帮助。
specification 在这个话题上(故意?)含糊不清,给实施者留下了很大的自由。因此,除非您所针对的 OpenCL 实现对标志做出明确保证,否则您应该将它们视为建议。
首先,CL_MEM_COPY_HOST_PTR
实际上与分配无关,它只是意味着你希望clCreateBuffer
用[=]处的内存内容预填充分配的内存12=] 你传递给了调用。这就好像你用 host_ptr = NULL
调用 clCreateBuffer
而没有这个标志,然后进行阻塞 clEnqueueWriteBuffer
调用来写入整个缓冲区。
关于分配模式:
CL_MEM_USE_HOST_PTR
- 这意味着您已经预先分配了一些内存,正确对齐,并希望将其用作缓冲区的后备内存。如果设备不支持直接访问主机内存,或者如果驱动程序决定将影子复制到 VRAM 比直接访问系统更有效,则实现仍然可以分配设备内存并在缓冲区和分配的内存之间来回复制记忆。但是,在 可以 直接从系统内存读取的实现中,这是零复制缓冲区的一个选项。
CL_MEM_ALLOC_HOST_PTR
- 这是一个提示,告诉 OpenCL 实现您计划通过将缓冲区映射到主机地址 space 来从主机端访问缓冲区,但不同于 [=16] =],您将分配本身留给了 OpenCL 实现。对于支持它的实现,这是零复制缓冲区的另一种选择:创建缓冲区,将其映射到主机,获取主机算法或 I/O 写入映射内存,然后取消映射并在GPU内核。与 CL_MEM_USE_HOST_PTR
不同,这为使用可以直接映射到 CPU 的地址 space(例如 PCIe BAR)的 VRAM 敞开了大门。
- 默认(以上2个都不是):分配给设备最方便的地方。通常是 VRAM,如果设备不支持内存映射到主机内存,这通常意味着如果您将其映射到主机地址 space,您最终会得到缓冲区的 2 个副本,一个在 VRAM 中,另一个在系统内存中,而 OpenCL 实现在 2.
之间来回内部复制
请注意,该实现还可以使用提供的任何访问标志(CL_MEM_HOST_WRITE_ONLY
、CL_MEM_HOST_READ_ONLY
、CL_MEM_HOST_NO_ACCESS
、CL_MEM_WRITE_ONLY
、CL_MEM_READ_ONLY
和 CL_MEM_READ_WRITE
) 影响分配内存的决定。
最后,关于 "pinned" 内存:许多现代系统都有 IOMMU,当它处于活动状态时,来自设备的系统内存访问会导致 IOMMU 页面错误,因此主机内存在技术上什至不需要常住。在任何情况下,OpenCL 实现通常与内核级设备驱动程序深度集成,通常可以按需固定系统内存范围(将它们排除在分页之外)。因此,如果使用 CL_MEM_USE_HOST_PTR
,您只需要确保提供适当对齐的内存,实现就会为您处理固定。
我是 OpenCL 的新手,虽然到目前为止我已经了解了所有内容,但我在理解缓冲区对象的工作原理时遇到了困难。
我一直不明白缓冲区对象存储在哪里。在 this Whosebug 问题中指出:
If you have one device only, probably (99.99%) is going to be in the device. (In rare cases it may be in the host if the device does not have enough memory for the time being)
对我来说,这意味着缓冲区对象存储在设备内存中。但是,如 this Whosebug 问题中所述,如果在 clCreateBuffer
中使用标志 CL_MEM_ALLOC_HOST_PTR
,则使用的内存很可能是固定内存。我的理解是,当内存被固定时,它不会被换出。这意味着固定内存必须位于 RAM 中,而不是设备内存中。
那么实际发生了什么?
我想知道flags是做什么的:
CL_MEM_USE_HOST_PTR
CL_MEM_COPY_HOST_PTR
CL_MEM_ALLOC_HOST_PTR
暗示缓冲区的位置。
谢谢
先来看看clCreateBuffer的签名:
cl_mem clCreateBuffer(
cl_context context,
cl_mem_flags flags,
size_t size,
void *host_ptr,
cl_int *errcode_ret)
这里没有论点可以为 OpenCL 运行时提供缓冲区应放入其内存中的确切设备,因为上下文可以有多个设备。运行时仅在我们使用缓冲区对象时才知道,例如read/write from/to 它,因为这些操作需要一个连接到特定设备的命令队列。
每个内存对象都驻留在主机内存或上下文设备的内存之一中,并且运行时可能会根据需要迁移它。 所以一般来说,每个内存对象在 OpenCL 运行时可能有一块内部主机内存。运行时实际做的是依赖于实现的,所以我们不能不做太多假设并得到没有便携式保证。这意味着有关固定等的一切都取决于实现,您只能希望最好,但要避免肯定会阻止使用固定内存的模式。
为什么我们要固定内存? 固定内存意味着,我们进程地址 space 中内存页的虚拟地址具有固定转换为 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
结合起来。运行时将为内部主机存储分配内存。 可以固定。
希望对您有所帮助。
specification 在这个话题上(故意?)含糊不清,给实施者留下了很大的自由。因此,除非您所针对的 OpenCL 实现对标志做出明确保证,否则您应该将它们视为建议。
首先,CL_MEM_COPY_HOST_PTR
实际上与分配无关,它只是意味着你希望clCreateBuffer
用[=]处的内存内容预填充分配的内存12=] 你传递给了调用。这就好像你用 host_ptr = NULL
调用 clCreateBuffer
而没有这个标志,然后进行阻塞 clEnqueueWriteBuffer
调用来写入整个缓冲区。
关于分配模式:
CL_MEM_USE_HOST_PTR
- 这意味着您已经预先分配了一些内存,正确对齐,并希望将其用作缓冲区的后备内存。如果设备不支持直接访问主机内存,或者如果驱动程序决定将影子复制到 VRAM 比直接访问系统更有效,则实现仍然可以分配设备内存并在缓冲区和分配的内存之间来回复制记忆。但是,在 可以 直接从系统内存读取的实现中,这是零复制缓冲区的一个选项。CL_MEM_ALLOC_HOST_PTR
- 这是一个提示,告诉 OpenCL 实现您计划通过将缓冲区映射到主机地址 space 来从主机端访问缓冲区,但不同于 [=16] =],您将分配本身留给了 OpenCL 实现。对于支持它的实现,这是零复制缓冲区的另一种选择:创建缓冲区,将其映射到主机,获取主机算法或 I/O 写入映射内存,然后取消映射并在GPU内核。与CL_MEM_USE_HOST_PTR
不同,这为使用可以直接映射到 CPU 的地址 space(例如 PCIe BAR)的 VRAM 敞开了大门。- 默认(以上2个都不是):分配给设备最方便的地方。通常是 VRAM,如果设备不支持内存映射到主机内存,这通常意味着如果您将其映射到主机地址 space,您最终会得到缓冲区的 2 个副本,一个在 VRAM 中,另一个在系统内存中,而 OpenCL 实现在 2. 之间来回内部复制
请注意,该实现还可以使用提供的任何访问标志(CL_MEM_HOST_WRITE_ONLY
、CL_MEM_HOST_READ_ONLY
、CL_MEM_HOST_NO_ACCESS
、CL_MEM_WRITE_ONLY
、CL_MEM_READ_ONLY
和 CL_MEM_READ_WRITE
) 影响分配内存的决定。
最后,关于 "pinned" 内存:许多现代系统都有 IOMMU,当它处于活动状态时,来自设备的系统内存访问会导致 IOMMU 页面错误,因此主机内存在技术上什至不需要常住。在任何情况下,OpenCL 实现通常与内核级设备驱动程序深度集成,通常可以按需固定系统内存范围(将它们排除在分页之外)。因此,如果使用 CL_MEM_USE_HOST_PTR
,您只需要确保提供适当对齐的内存,实现就会为您处理固定。