查看原文
其他

显存为什么不能当内存使?内存、Cache和Cache一致性

wolf uefiblog UEFI社区 2023-12-29
点击上方“公众号” 可以订阅哦!

外置显卡(独显)动辄8G以上显存,很多朋友都希望能够“借”一些给CPU当普通内存用。这在某种程度上是十分容易做到的。显存基本上都会被映射到PCI的mmio地址空间中,一个简单的驱动就可以将它们映射到普通的地址空间中,但如果在其上运行任何banchmark软件你就会发现性能相当差。这固然有GDDR和PC DDR设计初衷不同导致的问题,关于这部分以及之前DDR4的文章末尾部分网友的提问“为什么显存都DDR5了,内存还DDR4”,我后续还有一篇GDDR vs DDR vs HBM的文章来解释,敬请期待,这里就按下不表了。如果我们忽略GDDR的不同,一个进一步的问题就是,为什么不能通过PCIe来扩展普通内存?

主要的原因在于Cache。前一阵举行的“Interconnect Day 2019”,Intel宣布了一系列新技术。其中CXL(Compute Express Link)看起来并不显眼,但却是解决这个问题的关键:

CXL实际上有更大的野心:解决CPU和设备、设备和设备之间的memory鸿沟。普通电脑用户也许偶尔会想到用用显存,用不了也无伤大雅,这个需求并不强烈。但服务器用户有巨大的内存池和数量庞大的基于PCIe运算加速器,每个上面都有很大的内存。内存的分割已经造成巨大的浪费、不便和性能下降。CXL就是为解决这个问题而诞生,我迫不及待得想要给大家介绍这种新技术,但是为了更好的理解它,必须要有些预备知识,也是为什么偷显存性能低的原因:显存不能保证被cache,或者说无法保证cache的一致性。我刚好利用这次机会补上cache系列中缺失的一块拼图,来介绍一下cache一致性的问题。Cache的基础知识可以看这篇文章:Cache是怎么组织和工作的?

什么是Cache一致性?

Cache Memory简称Cache,是存储器子系统的组成部分,存放着程序经常使用的指令和数据,这就是Cache的传统定义。在最新的X86 CPU里,cache分为L1、L2和L3,L1一般还分成指令和数据两块,L3有时也被称作LLC(Last Level Cache)。Cache的各个层次之间内容可以是相互包含的(Inclusive),也可以是排斥的(Exclusive)。Inclusive和exclusive cache各有优缺点,比较复杂,以后单独讲,这里提到它们是因为它们和Cache一致性有一定关系,为了简化起见,这里所有相关性都被忽略,将来讲到Cache层级(Hierarchy)再来回顾。

CPU里面L3/LLC实际上被切成很多小片,每个Core对应一个小片:

Haswell-EP

这些小片在Ring bus上都有个Ring stop来连接,Ring bus和Ring bus之间的高速队列将这些L3小片整合在一起,形成一个虚拟的大一统L3。当然在Mesh network后不再有Ring bus,但L3的小片还是存在。我们来看统一后的两路情况:

如果我们不讨论Cache的层级,可以化简成这样:

假设我用红框标出的内存已经被Socket1/Node0和Socket2/Node1访问过了,它的部分数据已经被它们分别cache了。现在socket 1上的一个程序P1改写了一点这些内存中的内容,socket2上的另一个程序P2也要用这段内存。P1的改写和P2的读取如果都仅仅发生在各自的Cache中,就不能保证数据的全局一致性。换句话说就是在一个多处理器系统中,Cache们和内存池可能对同一份数据有多份副本,如何保证这些副本的一致性(Coherency)是个必须严肃对待的问题。

我们可以纯软件来处理这个问题,利用cache操作指令,但开销巨大十分复杂,而且操作系统的内存模型就需要全部改变,这对X86体系甚至绝大多数体系都是不能接受的。所以绝大多数计算机体系都是靠硬件来完成Cache Coherency的,硬件会自动保证各个副本的一致性,不需要软件操心。那么硬件是如何做到的呢?又有哪些弊病呢?

Cache一致性模型

X86、ARM和Power系列的Cache Coherency的原始模型都出自MESI protocol(参考资料2)。在MESI协议中,每个Cache Line(x86中是64 bytes)都有MESI四种状态:

MESI之间的转换可以表示为有限状态机的描述形态:

我并不打算相信介绍各个状态及它们之间的转换,对此有兴趣可以阅读参考资料2和其中的链接。

Intel、AMD和ARM都不是简简单单照搬MESI模型,而是在其上各有扩展,并结合一定的Directory来减小它带来的副作用。Intel的模型叫做MESIF,加了个Forward状态;AMD的模型叫做MOESI,加了个Owner状态。需要特别说明的是,即使同一个CPU,不同Cache层级会有不同的内存模型,这和Inclusive和exclusive密切相关,以后我们再来看有没有机会revisit这点。

Cache一致性发生在哪里?

Cache Line实际上是加了几个bits来表示这些状态。有了这些状态,那么是谁在管理这些状态,各个Cache Line的副本又是谁来同步的呢?在Intel CPU中,引入了两个新朋友:HA和CA。

Home Agent(HA),在内存控制器端;Cache Agent(CA),在L3 Cache端。他们都在Ring bus上监听和发送snoop消息。这种模型叫做Bus snooping模型,与之相对的还有Directory模型。Snoop消息会在QPI总线上广播,会造成很大的带宽消耗,为了减小这种带宽消耗,如何snoop有很多讲究,在参考资料1里面有介绍Intel的两种snoop的方式:Home Snoop和Source Snoop。它们的主要区别在于谁主导Snoop消息的发送,HA主导叫做Home Snoop,CA主导叫做Source Snoop。一个跨socket/node的Home Snoop例子:

结语

Intel每一代都在优化Snoop模型,有许多新的机制被引入,并结合Directory,来减小整体的overhead。尽管如此,snoop消耗的QPI带宽依然很高,这在8路变成16路甚至32路时会占据大量带宽,在很多情况下会让更多路变的得不偿失。

现在我们回头看看PCIe为什么不能够被用作真正的内存。因为PCIe和其他所有的设备一样,他们的memory不能被CPU cache。那么为什么不能被Cache呢?因为无法保证Cache一致性。

如前言所述,PCIe内部的memory的割裂性在服务器领域造成了很大问题,CXL的引入为解决这个问题提供了技术手段。加以时日,我相信普通的台式机也可以用上这种技术。关于CXL的介绍,将在下一篇文章中。

参考资料

[1]:https://www.intel.ca/content/dam/doc/white-paper/quick-path-interconnect-introduction-paper.pdf

[2]: https://en.wikipedia.org/wiki/MESI_protocol


继续滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存