OpenCL 内存传输函数的工作原理



我有几个问题,与 OpenCL 内存传输函数有关。我遇到了许多与此有关的问题,但没有一个给出扩展的答案。也许我们可以在这里收集总体答案。

这是我目前对三种当前移动数据方式的看法:

1) enqueueReadBuffer/enqueueWriteBuffer - 这两个函数始终将在主机上创建的缓冲区内容复制到设备以及从设备复制。此处不使用固定内存和 DMA 机制。

2) enqueueMigrateMemObjects - 这有时被描述为 enqueueRead/Write 的替代方法,但在这种情况下,内存正好在此函数调用时被复制。此处不使用固定内存和 DMA 机制。

3) enqueueMapBuffer/enqueueUnmapBuffer - 这里总是使用固定内存和DMA机制。

此函数使用两种类型的缓冲区:使用 CL_MEM_USE_HOST_PTR 标志或CL_MEM_ALLOC_HOST_PTR标志创建。对于第一个,我们将在主机上创建的数组映射到在设备上创建的数组。在设备上分配第二个阵列,并将其映射到主机上新创建的阵列。

这是我可以根据文档声明的内容。我运行了几次测试,但只看到迁移功能比读/写更快。 关于这三段,我有一些疑问:

1)如果这些功能只做复制,那么为什么 https://software.intel.com/en-us/forums/opencl/topic/509406 人们在这里谈论在读取/写入过程中固定/取消固定内存?这些函数在哪些情况下使用固定内存?或者这只是英特尔实施的功能,其中所有与内存传输相关的功能都使用固定内存和 DMA?

另外,这是否意味着,如果我使用固定内存,则 DMA 机制将起作用?反之亦然 - 如果我想让 DMA 工作,我需要固定内存?

2) 这个迁移函数 - 正是在 enqueueRead/WriteBuffer 函数内部发生了什么,而这些 enqueuRead/writeBuffer 函数提供了一些额外的开销?它总是只是复制还是也进行 DMA 传输?

出于某些原因,某些来源在谈论DMA传输时,使用"复制","内存","迁移"一词在两个缓冲区(主机和设备上)之间传输数据。但是,不能有任何副本,我们只是直接写入缓冲区,根本不需要任何副本。在 DMA 期间,我应该如何处理此写入?

如果我将 enqueueMigrateMemOjects 与缓冲区一起使用,会发生什么情况,并使用标志CL_MEM_USE_HOST_PTR创建?

3)有了这两个功能,就完全混乱了。如果我使用:a) 现有主机指针或 b) 新分配的主机指针,映射和读取/写入将如何发生?

同样在这里,我没有正确理解 DMA 的工作原理。如果我将主机端的缓冲区映射到设备端的缓冲区,则借助哪些功能在它们之间传输内存?之后我应该总是取消映射我的缓冲区吗?

在任何地方都没有解释,例如:"当我们使用此标志创建一个新缓冲区并使用此内存函数传输时,数据以这种方式传输,并且具有以下功能...被使用。如果内存创建为只读,则会发生这种情况,如果内存仅写入 - this"。

也许已经有一个很好的指南,但是从OpenCL规范来看,我无法回答我的问题。

1) DMA 用于所有数据传输命令,但它仅适用于固定内存区域。否则,操作系统只会将其分页并提供虚假数据。

在enqueueReadBuffer/enqueueWriteBuffer中,数据首先进入已经固定的内部缓冲区(或者及时固定,我不知道),然后使用DMA进入GPU内存。因为 GPU 可以在页面中发送或接收数据,例如大小为 4096 的整数倍,起始地址为 4096 的倍数等(取决于供应商的对齐规则)。ReadBuffer 和 WriteBuffer 可能会更慢,因为它执行两个副本,一个从主机数组到内部数组,然后从内部固定数组到 gpu 缓冲区。

在 enqueueMapBuffer/enqueueUnmapBuffer 中,它可以直接有效地执行 DMA 传输,因为它刷新仅从主机端使用的页面(您写入主机阵列的位置,但由于它已映射,因此它被上传到 GPU 缓冲区),因为它临时固定区域,只有一次

使用 CL_MEM_ALLOC_HOST_PTR 或 CL_MEM_USE_HOST_PTR 只能是一种优化,以跳过内部(固定)数组复制步骤,但要确保它们符合要求。不能保证总是固定数组,它们是一种相当稀缺的资源。固定意味着操作系统不会随时将其分页。您也可以只是"触摸"缓冲区的页面(4kB?)区域的第一个字节以伪造"固定"(在调用数据传输函数之前),但这是不合法的,并且可能并不总是有效。但我在我的 OpenCL 应用程序中观察到加速,只需在USE_HOST_PTR标记的缓冲区上提供良好的偏移量和适当的复制大小(如 4k 对齐和 64k 大小)。(仅在AMD,NVIDIA GPU和Intel IGPU上尝试过,但不能说Xeon Phi设备)

只需固定内存即可跳过额外的复制开销。您需要映射/取消映射来优化固定。

2) 迁移功能将 GPU 缓冲区的所有权迁移到另一个 GPU。如果你在同一个GPU上使用它,除了将自身复制到RAM然后再次复制自己之外,它不应该产生任何有用的东西。如果您使用CL_MIGRATE_MEM_OBJECT_- CONTENT_UNDEFINED则它不会复制数据。只需将所有权转移到其他设备即可。然后你可以自己做数据复制(如果你的意思是想要的数据在主机上,而不是源设备上)

一些实现通过直接 pci-e 复制其数据(我不知道这是否使用 GPU1 DMA 到 GPU2 DMA,但我想是的),一些实现使用双 DMA 操作通过 RAM(但它可以通过流水线在某种程度上进行优化?

会发生什么,如果我将 enqueueMigrateMemOjects 与缓冲区一起使用, 用旗帜CL_MEM_USE_HOST_PTR创建?

我没有尝试,但猜测它只会移动所有权,因为数据仅在主机上。此外,您提供的英特尔链接还包括有人说"迁移触发 DMA 操作"。当使用两个 Phi 之间的迁移时,默认情况下,两个英特尔至强融核可能与 DMA 通信,而不是通过系统 RAM。

3)CL_MEM_USE_HOST_PTR旨在与应用程序的指针一起使用。它们可以是任何东西,甚至是固定的(但在OpenCL的内部规则之外,这可能并不总是好的)。 如果可以的话,CL_MEM_ALLOC_HOST_PTR旨在使用 OpenCL 实现的固定内存。如果您仅使用CL_MEM_READ_WRITE则它位于设备内存中。使用CL_MEM_USE_HOST_PTRCL_MEM_ALLOC_HOST_PTR意味着如果 OpenCL 内核直接与 CPU 共享 RAM(例如,如果设备是 iGPU),它将进行零拷贝访问。 无需固定,额外复制。使用固定时,iGPU 不会复制。差别非常大。但是对于独立的GPU(或像Xeon Phi这样的计算卡),它的速度可能是额外复制版本的1.0倍到2.0倍(考虑到主机到主机和主机到设备的副本具有相似的带宽)。

映射是指主机端映射。主机看到设备内存"映射"到自己的内存。所以设备无法访问它(例如通过内核)。写入意味着主机端写入。主机写入 GPU 内存。阅读意味着主机阅读。主机从 GPU 内存读取。因此,地图取消映射是这样发生的:

CL_MAP_WRITE_INVALIDATE_REGION版本:

  • 将主机指针(由 map 命令返回)映射到设备缓冲区
    • 现在缓冲区所有权在主机上
  • 使用 GPU 缓冲区,就好像它是这个主机指针一样
  • 取消映射该区域,以便它将最新位刷新到 GPU 内存(或者,如果是 iGPU,则无操作!!
    • 现在缓冲区所有权在设备上

还有另一种(CL_MAP_WRITE)用法

  • 您事先准备数据
  • 映射(启用初始复制)(获取所有数据)//额外开销
  • 可选元素"更新">
  • 取消映射
  • 现在数据(可选更新)在 GPU 内存上
  • 以便 OpenCL 内核函数可以将其用作参数

在映射和取消映射之间,它可以将您输入的任何主机输入刷新到 GPU 内存中,因为它会立即固定整个区域。否则,它将(如在enqueueWriteBuffer中)需要固定取消固定任何当前数据(如单个整数)(及其整个页面)发送到GPU,并且速度会很慢。

复制 1GB 缓冲区时,内存消耗最多不超过 2GB。它处理较小内部阵列(如 64kB)上的引脚-取消固定-额外复制操作,但取决于 OpenCL 的供应商实现。

但是在enqueueWriteBuffer上,它使用内部缓冲区进行必要的复制和固定。固定和取消固定以及执行额外复制都会使 enqueueWriteBuffer 变慢,但您仍然可以尝试为其提供正确对齐的主机指针和适当大小的区域以更快地执行复制。至少这将使它只在后台对整个数组进行一次固定。如果实现有优化,甚至可能跳过额外的副本。

最新更新