GPU上的相干内存是啥?

Posted

技术标签:

【中文标题】GPU上的相干内存是啥?【英文标题】:What is coherent memory on GPU?GPU上的相干内存是什么? 【发布时间】:2016-07-14 10:52:34 【问题描述】:

我没有一次偶然发现

tech papers 与图形编程相关。我一直在寻找一个简单明了的解释,但发现大多是this 类型的“硬核”论文。我很高兴收到关于什么是相干内存的外行风格的回答关于 GPU 架构以及它与其他(可能是不连贯的)内存类型的比较。

【问题讨论】:

“连贯”意味着如果一个数据对象被多个代理(或多条路径)访问,每个代理将看到完全相同的状态。这两个代理可能是一个 CPU 和一个 GPU。两个读取路径的示例可能是通过纹理缓存与 L1 缓存。保持一致性通常需要额外的硬件机制,例如用于跟踪缓存行的 MESI 或 MOESI 状态的位,并且可能导致大量的一致性流量用于传送数据,尤其是在有很多代理的情况下。 GPU 中的纹理缓存是“不连贯”机制的典型示例。如果纹理映射底层的数据发生变化,则纹理缓存中的任何缓存内容都可能不会失效或刷新,随后对纹理缓存的访问会导致读取过时的数据。 【参考方案1】:

记忆就是记忆。但是不同的东西可以访问该内存。 GPU 可以访问内存,CPU 可以访问内存,或者其他硬件位,等等。

如果其他人对该记忆所做的更改对读者可见,则该特定事物具有对记忆的“连贯”访问。现在,你可能认为这是愚蠢的。毕竟,如果记忆已经改变,怎么会有人可能看不到它呢?

简单地说,缓存。

事实证明,更换内存的成本很高。所以我们尽一切可能避免改变内存,除非我们绝对必须这样做。当您从 CPU 将单个字节写入内存中的指针时,CPU 还没有写入该字节。或者至少,不记得。它将其写入该内存的本地副本,称为“缓存”。

这样做的原因是,一般来说,应用程序不会写入(或读取)单个字节。他们更有可能以小块的形式写入(和读取)大量字节。因此,如果您要执行诸如内存加载或存储之类的昂贵操作,您应该加载或存储大量内存。因此,您将要对一块内存进行的所有更改都存储在缓存中,然后在将来的某个时间将该缓存块写入实际内存。

但是,如果您有两个使用相同内存的独立设备,则需要某种方式来确保一个设备所做的写入对其他设备可见。大多数 GPU 无法读取 CPU 缓存。而且大多数 CPU 语言没有语言级别的支持来说“嘿,我写到内存中的那些东西?我真的是想让你现在把它写到内存中。”因此,您通常需要一些东西来确保更改的可见性。

在 Vulkan 中,标记为 VK_MEMORY_PROPERTY_HOST_COHERENT_BIT 的内存意味着,如果您读/写该内存(通过映射指针,因为这是 Vulkan 让您直接写入内存的唯一方式),您 不要t 需要使用函数vkInvalidateMappedMemoryRanges/vkFlushMappedMemoryRanges 来确保CPU/GPU 可以看到这些变化。任何更改的可见性都可以在两个方向上得到保证。如果该标志在内存中不可用,那么您必须使用上述功能来确保您要访问的特定数据区域的一致性。

对于连贯内存,硬件方面会发生两件事之一。 CPU 对内存的访问没有缓存在任何 CPU 的缓存中,或者 GPU 可以直接访问 CPU 的缓存(可能是由于与 CPU 位于同一芯片上)。您通常可以看出后者正在发生,因为 Vulkan 的片上 GPU 实现不会费心提供不连贯的内存选项。

【讨论】:

“VK_MEMORY_PROPERTY_HOST_COHERENT_BIT 位指定主机缓存管理命令 vkFlushMappedMemoryRanges 和 vkInvalidateMappedMemoryRanges 不需要分别刷新主机写入到设备或使设备写入对主机可见。” - Vulkan 规格。所以它显然是双向的。 @DanielRussell 我编辑了答案以包含此内容,因为答案给人的印象是主机一致性仅影响 CPU->GPU 写入,反之亦然。【参考方案2】:

如果内存是一致的,那么访问该内存的所有线程必须始终就内存的状态达成一致,例如:如果线程 0 读取内存位置 A 并且线程 1 同时读取相同的位置,则两个线程应该始终读取相同的值。

但是如果内存不连贯,那么线程 A 和 B 可能会读回不同的值。线程 0 可能认为位置 A 包含 1,而线程认为该位置包含 2。不同的线程可能对内存有不连贯的看法。

使用大量内核很难实现连贯性。通常每个内核都必须知道来自所有其他内核的内存访问。因此,如果您在四核 CPU 中有 4 个核心,那么一致性并不难实现,因为每个核心都必须了解其他 3 个核心的内存访问地址,但是在具有 16 个核心的 GPU 中,必须让每个核心都知道其他 15 个内核访问内存。核心使用所谓的“缓存一致性协议”交换有关其缓存内容的数据。

这就是为什么 GPU 通常只支持有限形式的一致性。如果某些内存位置是只读的或仅由单个线程访问,则不需要一致性。如果缓存很小并且并不总是需要一致性,而只是在程序的特定指令处,那么可以在特定内存访问之前或之后使用缓存刷新来实现程序的正确行为。

如果您的硬件同时提供一致性和非一致性内存类型,那么您可以预期非一致性内存会更快,但如果您尝试使用此内存运行并行算法,它们将以非常奇怪的方式失败。

【讨论】:

以上是关于GPU上的相干内存是啥?的主要内容,如果未能解决你的问题,请参考以下文章

学习共享内存、分布式内存和/或 GPU 编程的典型问题和解决方案是啥?

Google Colab 上的 GPU 内存不足错误消息

keras上的多GPU训练错误(OOM)(足够的内存,可能是配置问题)

CUDA 学习(十三)全局内存

僵尸进程不允许释放 GPU 内存

内存上的Shared Memory是啥?