OpenCL 内存传输函数如何工作?
How OpenCL memory transfer functions work?
我有几个问题,与 OpenCL 内存 t运行sfer 函数有关。我遇到了很多与此相关的问题,但是 none 给出了扩展答案。大概我们可以在这里收集整体答案。
这是我目前对三种移动数据方式的看法:
1) enqueueReadBuffer/enqueueWriteBuffer - 这两个函数总是将在主机上创建的缓冲区的内容复制到设备,以及从设备复制。这里没有使用固定内存,也没有使用 DMA 机制。
2) enqueueMigrateMemObjects - 这有时被描述为 enqueueRead/Write 的替代方法,但在这种情况下,内存会在调用此函数时准确复制。这里没有使用固定内存,也没有使用 DMA 机制。
3) enqueueMapBuffer/enqueueUnmapBuffer - 这里总是使用固定内存和 DMA 机制。
此函数使用两种类型的缓冲区:使用 CL_MEM_USE_HOST_PTR 标志或 CL_MEM_ALLOC_HOST_PTR 标志创建。对于第一个,我们将在主机上创建的数组映射到在设备上创建的数组。第二个数组在设备上分配并将其映射到主机上新创建的数组。
这是我根据文档可以说明的。我 运行 测试了几次,但只看到迁移功能比 reading/writing 快。
关于这三段我有一些疑问:
1) 如果这些函数只做复制,那为什么这里 https://software.intel.com/en-us/forums/opencl/topic/509406 人们在 reading/writing 期间谈论 pinning/unpinning 内存?这些函数在什么情况下使用固定内存?或者这只是英特尔实现的特性,其中所有内存 t运行sfer 相关功能都使用固定内存和 DMA?
此外,这是否意味着,如果我使用固定内存,那么 DMA 机制将起作用?反之亦然 - 如果我想让 DMA 工作,我需要固定内存吗?
2) 这个迁移函数是在 enqueueRead/WriteBuffer 函数内部发生的事情,没有一些额外的开销,这些 enqueuRead/writeBuffer 函数提供了什么?它总是只是复制还是也做 DMA t运行sfer?
出于某些原因,一些来源在谈论 DMA t运行sfer 时,使用 "copy"、"memory"、"migration" 词来表示 t运行sferring两个缓冲区(在主机和设备上)之间的数据。但是,不可能有任何副本,我们只是直接写入缓冲区而根本没有任何副本。我应该如何在 DMA 期间处理此写入?
如果我将 enqueueMigrateMemOjects 与缓冲区一起使用,使用标志 CL_MEM_USE_HOST_PTR 创建,会发生什么?
3) 有了这两个函数,完全是一头雾水。如果我使用:a) 现有主机指针或 b) 新分配的主机指针,映射和 reading/writing 将如何发生?
这里我也没有正确理解 DMA 的工作原理。如果我将主机端的缓冲区映射到设备端的缓冲区,在哪些函数的帮助下,内存在它们之间转移运行?之后我应该总是取消映射我的缓冲区吗?
对此没有任何解释,例如:“当我们用这个标志创建一个新缓冲区并使用这个内存函数 t运行sfer 时,数据就是这样传输的 t运行s并使用诸如...等功能。如果内存创建为只读,则会发生这种情况,如果内存只写 - this".
也许这方面已经有很好的指南,但从 OpenCL 规范来看,我无法回答我的问题。
1) DMA 用于所有数据传输命令,但它仅适用于固定内存区域。否则 OS 只会将其分页并提供错误数据。
在 enqueueReadBuffer/enqueueWriteBuffer 中,数据首先进入一个已经固定(或及时固定,我不知道)的内部缓冲区,然后使用 DMA 进入 GPU 内存。因为 GPU 可以在页面中发送或接收数据,例如大小为 4096 的整数倍,起始地址为 4096 的倍数等(取决于供应商的对齐规则)。 ReadBuffer 和 WriteBuffer 可能会比较慢,因为它有两个副本,一个从主机数组到内部数组,然后是内部固定数组到 gpu 缓冲区。
在enqueueMapBuffer/enqueueUnmapBuffer中,它可以直接高效地进行 DMA 传输,因为它刷新仅在主机端使用的页面(您写入主机数组但由于它已映射,因此会上传到 gpu缓冲区),因为它暂时固定该区域,只有一次。
使用CL_MEM_ALLOC_HOST_PTR 或CL_MEM_USE_HOST_PTR 只能作为跳过内部(固定)数组复制步骤的优化,但要确保它们符合要求。不能保证总是获得固定数组,它们是一种相当稀缺的资源。固定意味着 OS 不会随时将其分页。您也可以只 "touch" 到缓冲区的页面(4kB?)区域的第一个字节来伪造 "pinning" (在调用数据传输函数之前)但它是不合法的并且可能并不总是有效。但是我在我的 OpenCL 应用程序中观察到加速,只需在 USE_HOST_PTR 标记的缓冲区上提供良好的偏移量和良好的复制大小(如 4k 对齐和 64k 大小)。 (仅在 AMD、NVIDIA GPU 和 Intel IGPU 上试过,但不能说 Xeon Phi 设备)
您只需要固定内存即可跳过额外的复制开销。您需要 map/unmap 来优化固定。
2) 迁移函数将 GPU 缓冲区的所有权迁移到另一个 GPU。如果你在同一个 GPU 上使用它,除了将自身复制到 RAM 然后再将自身复制回来之外,它不应该做任何有用的事情。如果您使用 CL_MIGRATE_MEM_OBJECT_- CONTENT_UNDEFINED
则它不会复制数据。只是将所有权转移到其他设备。然后你可以自己复制数据(如果你的意思是想要的数据在主机上,而不是源设备上)
一些实现直接通过 pci-e 复制它的数据(我不知道这是否使用 GPU1 DMA 到 GPU2 DMA 但我想是的)一些实现通过 RAM 使用双 DMA 操作(但它可以被优化通过某种程度上的流水线?)
What will happen, if I will use enqueueMigrateMemOjects with buffers,
created with flag CL_MEM_USE_HOST_PTR?
我没有尝试,但猜测它只会移动所有权,因为数据仅在主机上。此外,您提供的英特尔 link 包括有人说 "migration triggers DMA operation"。当使用两个 Phis 之间的迁移时,默认情况下,可能两个 Intel Xeon Phi 与 DMA 通信而不是通过系统 RAM。
3) CL_MEM_USE_HOST_PTR
用于处理您的应用程序的指针。它们可以是任何东西,甚至是固定的(但在 OpenCL 的内部规则之外,这可能并不总是很好)。 CL_MEM_ALLOC_HOST_PTR
旨在使用 OpenCL 实现的固定内存(如果可以的话)。如果您只使用 CL_MEM_READ_WRITE 那么它就在设备内存中。使用 CL_MEM_USE_HOST_PTR
和 CL_MEM_ALLOC_HOST_PTR
意味着如果 OpenCL 内核直接与 CPU 共享 RAM(例如,如果设备是 iGPU),它将进行零拷贝访问。没有固定,额外的副本。通过固定,iGPU 不会复制。非常大的区别。但是对于独立的 GPU(或 Xeon Phi 之类的计算卡),它可能是额外复制版本速度的 1.0 倍到 2.0 倍之间(考虑到主机到主机和主机到设备的副本具有相似的带宽)。
映射表示主机端映射。主机看到自己的设备内存 "mapped"。所以设备无法访问它(例如通过内核)。写入意味着主机端写入。主机写入 GPU 内存。读书,就是主人读书。主机从 GPU 内存读取。所以,映射取消映射发生这样的:
CL_MAP_WRITE_INVALIDATE_REGION
版本:
- 你将主机指针(由 map 命令返回)映射到设备缓冲区
- 现在缓冲区所有权在主机上
- 使用 GPU 缓冲区,就好像它是这个主机指针一样
- 取消映射该区域,以便将最新位刷新到 GPU 内存(或者,如果是 iGPU,则无操作!!)
- 现在缓冲区所有权在设备上
还有另一种(CL_MAP_WRITE
)用法
- 你事先准备好数据
- map(启用初始复制)(获取所有数据)//额外开销
- 可选元素"updates"
- 取消映射
- 现在数据(可选更新)在 GPU 内存中
- 以便 OpenCL 内核函数可以将其用作参数
在映射和取消映射之间,它可以将您输入的任何主机输入刷新到 GPU 内存中,因为它会同时固定整个区域。否则它将(如在 enqueueWriteBuffer 中)需要固定取消固定任何当前数据(如单个整数)(及其整个页面)被发送到 GPU,并且它会很慢。
复制 1GB 缓冲区时,内存消耗不会达到 2GB。它在较小的内部阵列上处理 pin-unpin-extra-copy 操作,大约 64kB,但取决于 OpenCL 的供应商实现。
但是在 enqueueWriteBuffer 上,它使用内部缓冲区来进行必要的复制和固定。固定和取消固定以及进行额外的复制都会使 enqueueWriteBuffer 变慢,但您仍然可以尝试为其提供正确对齐的主机指针和适当大小的区域以更快地进行复制。至少这会让它在后台对整个阵列只进行一次固定。如果实现有优化,甚至可能会跳过额外的副本。
我有几个问题,与 OpenCL 内存 t运行sfer 函数有关。我遇到了很多与此相关的问题,但是 none 给出了扩展答案。大概我们可以在这里收集整体答案。
这是我目前对三种移动数据方式的看法:
1) enqueueReadBuffer/enqueueWriteBuffer - 这两个函数总是将在主机上创建的缓冲区的内容复制到设备,以及从设备复制。这里没有使用固定内存,也没有使用 DMA 机制。
2) enqueueMigrateMemObjects - 这有时被描述为 enqueueRead/Write 的替代方法,但在这种情况下,内存会在调用此函数时准确复制。这里没有使用固定内存,也没有使用 DMA 机制。
3) enqueueMapBuffer/enqueueUnmapBuffer - 这里总是使用固定内存和 DMA 机制。
此函数使用两种类型的缓冲区:使用 CL_MEM_USE_HOST_PTR 标志或 CL_MEM_ALLOC_HOST_PTR 标志创建。对于第一个,我们将在主机上创建的数组映射到在设备上创建的数组。第二个数组在设备上分配并将其映射到主机上新创建的数组。
这是我根据文档可以说明的。我 运行 测试了几次,但只看到迁移功能比 reading/writing 快。 关于这三段我有一些疑问:
1) 如果这些函数只做复制,那为什么这里 https://software.intel.com/en-us/forums/opencl/topic/509406 人们在 reading/writing 期间谈论 pinning/unpinning 内存?这些函数在什么情况下使用固定内存?或者这只是英特尔实现的特性,其中所有内存 t运行sfer 相关功能都使用固定内存和 DMA?
此外,这是否意味着,如果我使用固定内存,那么 DMA 机制将起作用?反之亦然 - 如果我想让 DMA 工作,我需要固定内存吗?
2) 这个迁移函数是在 enqueueRead/WriteBuffer 函数内部发生的事情,没有一些额外的开销,这些 enqueuRead/writeBuffer 函数提供了什么?它总是只是复制还是也做 DMA t运行sfer?
出于某些原因,一些来源在谈论 DMA t运行sfer 时,使用 "copy"、"memory"、"migration" 词来表示 t运行sferring两个缓冲区(在主机和设备上)之间的数据。但是,不可能有任何副本,我们只是直接写入缓冲区而根本没有任何副本。我应该如何在 DMA 期间处理此写入?
如果我将 enqueueMigrateMemOjects 与缓冲区一起使用,使用标志 CL_MEM_USE_HOST_PTR 创建,会发生什么?
3) 有了这两个函数,完全是一头雾水。如果我使用:a) 现有主机指针或 b) 新分配的主机指针,映射和 reading/writing 将如何发生?
这里我也没有正确理解 DMA 的工作原理。如果我将主机端的缓冲区映射到设备端的缓冲区,在哪些函数的帮助下,内存在它们之间转移运行?之后我应该总是取消映射我的缓冲区吗?
对此没有任何解释,例如:“当我们用这个标志创建一个新缓冲区并使用这个内存函数 t运行sfer 时,数据就是这样传输的 t运行s并使用诸如...等功能。如果内存创建为只读,则会发生这种情况,如果内存只写 - this".
也许这方面已经有很好的指南,但从 OpenCL 规范来看,我无法回答我的问题。
1) DMA 用于所有数据传输命令,但它仅适用于固定内存区域。否则 OS 只会将其分页并提供错误数据。
在 enqueueReadBuffer/enqueueWriteBuffer 中,数据首先进入一个已经固定(或及时固定,我不知道)的内部缓冲区,然后使用 DMA 进入 GPU 内存。因为 GPU 可以在页面中发送或接收数据,例如大小为 4096 的整数倍,起始地址为 4096 的倍数等(取决于供应商的对齐规则)。 ReadBuffer 和 WriteBuffer 可能会比较慢,因为它有两个副本,一个从主机数组到内部数组,然后是内部固定数组到 gpu 缓冲区。
在enqueueMapBuffer/enqueueUnmapBuffer中,它可以直接高效地进行 DMA 传输,因为它刷新仅在主机端使用的页面(您写入主机数组但由于它已映射,因此会上传到 gpu缓冲区),因为它暂时固定该区域,只有一次。
使用CL_MEM_ALLOC_HOST_PTR 或CL_MEM_USE_HOST_PTR 只能作为跳过内部(固定)数组复制步骤的优化,但要确保它们符合要求。不能保证总是获得固定数组,它们是一种相当稀缺的资源。固定意味着 OS 不会随时将其分页。您也可以只 "touch" 到缓冲区的页面(4kB?)区域的第一个字节来伪造 "pinning" (在调用数据传输函数之前)但它是不合法的并且可能并不总是有效。但是我在我的 OpenCL 应用程序中观察到加速,只需在 USE_HOST_PTR 标记的缓冲区上提供良好的偏移量和良好的复制大小(如 4k 对齐和 64k 大小)。 (仅在 AMD、NVIDIA GPU 和 Intel IGPU 上试过,但不能说 Xeon Phi 设备)
您只需要固定内存即可跳过额外的复制开销。您需要 map/unmap 来优化固定。
2) 迁移函数将 GPU 缓冲区的所有权迁移到另一个 GPU。如果你在同一个 GPU 上使用它,除了将自身复制到 RAM 然后再将自身复制回来之外,它不应该做任何有用的事情。如果您使用 CL_MIGRATE_MEM_OBJECT_- CONTENT_UNDEFINED
则它不会复制数据。只是将所有权转移到其他设备。然后你可以自己复制数据(如果你的意思是想要的数据在主机上,而不是源设备上)
一些实现直接通过 pci-e 复制它的数据(我不知道这是否使用 GPU1 DMA 到 GPU2 DMA 但我想是的)一些实现通过 RAM 使用双 DMA 操作(但它可以被优化通过某种程度上的流水线?)
What will happen, if I will use enqueueMigrateMemOjects with buffers, created with flag CL_MEM_USE_HOST_PTR?
我没有尝试,但猜测它只会移动所有权,因为数据仅在主机上。此外,您提供的英特尔 link 包括有人说 "migration triggers DMA operation"。当使用两个 Phis 之间的迁移时,默认情况下,可能两个 Intel Xeon Phi 与 DMA 通信而不是通过系统 RAM。
3) CL_MEM_USE_HOST_PTR
用于处理您的应用程序的指针。它们可以是任何东西,甚至是固定的(但在 OpenCL 的内部规则之外,这可能并不总是很好)。 CL_MEM_ALLOC_HOST_PTR
旨在使用 OpenCL 实现的固定内存(如果可以的话)。如果您只使用 CL_MEM_READ_WRITE 那么它就在设备内存中。使用 CL_MEM_USE_HOST_PTR
和 CL_MEM_ALLOC_HOST_PTR
意味着如果 OpenCL 内核直接与 CPU 共享 RAM(例如,如果设备是 iGPU),它将进行零拷贝访问。没有固定,额外的副本。通过固定,iGPU 不会复制。非常大的区别。但是对于独立的 GPU(或 Xeon Phi 之类的计算卡),它可能是额外复制版本速度的 1.0 倍到 2.0 倍之间(考虑到主机到主机和主机到设备的副本具有相似的带宽)。
映射表示主机端映射。主机看到自己的设备内存 "mapped"。所以设备无法访问它(例如通过内核)。写入意味着主机端写入。主机写入 GPU 内存。读书,就是主人读书。主机从 GPU 内存读取。所以,映射取消映射发生这样的:
CL_MAP_WRITE_INVALIDATE_REGION
版本:
- 你将主机指针(由 map 命令返回)映射到设备缓冲区
- 现在缓冲区所有权在主机上
- 使用 GPU 缓冲区,就好像它是这个主机指针一样
- 取消映射该区域,以便将最新位刷新到 GPU 内存(或者,如果是 iGPU,则无操作!!)
- 现在缓冲区所有权在设备上
还有另一种(CL_MAP_WRITE
)用法
- 你事先准备好数据
- map(启用初始复制)(获取所有数据)//额外开销
- 可选元素"updates"
- 取消映射
- 现在数据(可选更新)在 GPU 内存中
- 以便 OpenCL 内核函数可以将其用作参数
在映射和取消映射之间,它可以将您输入的任何主机输入刷新到 GPU 内存中,因为它会同时固定整个区域。否则它将(如在 enqueueWriteBuffer 中)需要固定取消固定任何当前数据(如单个整数)(及其整个页面)被发送到 GPU,并且它会很慢。
复制 1GB 缓冲区时,内存消耗不会达到 2GB。它在较小的内部阵列上处理 pin-unpin-extra-copy 操作,大约 64kB,但取决于 OpenCL 的供应商实现。
但是在 enqueueWriteBuffer 上,它使用内部缓冲区来进行必要的复制和固定。固定和取消固定以及进行额外的复制都会使 enqueueWriteBuffer 变慢,但您仍然可以尝试为其提供正确对齐的主机指针和适当大小的区域以更快地进行复制。至少这会让它在后台对整个阵列只进行一次固定。如果实现有优化,甚至可能会跳过额外的副本。