为什么CPU有多个缓存级别?
https://fgiesen.wordpress.com/2016/08/07/why-do-cpus-have-multiple-cache-levels/
2016年8月7日
这是来自“jlforrest”的读者问题,似乎值得回答的问题不仅仅是一句话:
我理解需要缓存,但我不明白为什么有多级缓存而不是只有一个更大的级别。换句话说,假设L1缓存为32K,L2缓存为256K,L3缓存为2M,为什么没有单个32K + 256K + 2M L1缓存?
简短版本是各种缓存级别在设计方式上有很大差异;它们受到不同的限制并满足不同的目的。根据经验,当您爬上缓存层次结构的级别时,缓存会变得更大,更慢,密度更高(每单位面积存储的位数更多),每位存储的功耗更低,并获得额外的任务。
为了在这里建立一些直觉,让我们从一个精心设计和有些古怪的类比开始。没错,它是......
缓存故事时间!
假设你是20世纪60年代一些无名的官僚机构中的白领办公室工作人员,没有看到计算机,你的工作涉及很多查看和交叉引用案件文件(这里是包含纸张的文件夹)。
你有一张桌子(L1数据缓存)。在您的桌面上是您正在处理的文件(缓存行),以及您最近提取的其他文件,这些文件已完成或预计会再次查看。使用文件通常意味着查看它包含的各个页面(对应于缓存行中的字节)。但除非他们在你的桌子上,否则文件只会被视为一个单元。你总是一次抓取整个文件,即使它们中只有一页你真正关心的。
办公室里还有一个文件柜(L2缓存)。该文件柜包含您最近处理过的文件,但现在没有使用。当您完成桌面上的内容后,大部分文件将重新进入该文件柜。从柜子里抓东西不是立竿见影的 - 你需要走到那里,打开正确的抽屉和拇指通过一些索引卡找到合适的文件 - 但它仍然很快。
有时其他人需要查看文件柜中的文件。有一个人带着推车,巴斯特(代表一种环形巴士)只是在所有的办公室里不停地进行巡视。当办公室工作人员需要一个他们没有私人文件柜的文件时,他们只需要写一个小的请求单并交给Buster。为简单起见,让我们说巴斯特知道一切都在哪里。因此,下次他到你的办公室时,巴斯特会检查是否有人要求你柜子里的任何文件,如果是这样的话,就会默默地将这些文件从机柜中取出并放在他的推车上。下次他来到办公室时,无论谁请求文件,他都会把文件放在他们的柜子里,并在桌子上留下收据。
每隔一段时间,Buster就会注意到所请求的文件不在内阁中,而是在桌面上。在那种情况下,他不能只是默默地抓住它;他需要问办公桌上的工人是否已经完成了工作,如果不是,那个工人和提出请求的工人需要就该做什么达成一致。关于在那种情况下该做什么,有繁琐复杂的公司协议(会议将被确定!)。
文件柜通常是满的。这意味着巴斯特不能只是放入一个新文件;他必须首先制造空间,他通过抓住另一个文件,最好是一段时间没用过的文件。这些文件,Buster进入地下室的存档(L3缓存)。在地下室中,文件组被密集地包装在工业货架单元的纸板箱中。正规办公室工作人员根本不会来这里;它完全不受影响,他们不熟悉备案系统的细节。我们把它留给档案文员。
每当巴斯特来到这里时,他就会丢弃他在前台“进”中的托盘中存档的所有旧文件。他还会删除所有不在楼上任何文件柜中的文件的请求单,但他不会等到文员带回文件,因为它需要一段时间。他们只是采取请求单,拿起有问题的文件,并在完成后将其放在“外出”托盘中。因此,每次巴斯特来到这里时,他都会抓住“外出”托盘中的任何东西,将其放在购物车上,并在下次来时将其带到收件人手中。
现在,问题是,有很多这些文件,即使有效的包装,它们甚至不能接近地下室。大多数文件都在异地;这是一个办公楼,位于城镇的一个不错的地方,这里的租金太高,无法在存储上花费那么多空间。相反,该公司在仓库外30分钟出租仓库空间,其中保留了大部分旧文件(这对应于DRAM)。在地下室的前台坐着头部档案管理员梅根。 Megan会跟踪哪些文件保存在地下室中,哪些文件是存储的。因此,当巴斯特放弃他的请求在“in”托盘中滑动时,她检查哪一个对应于地下室中的文件(由档案办事员处理),哪些不在现场。后者只是被添加到一大堆请求中。也许每天一两次,他们将一辆面包车送到仓库以获取所请求的文件,以及相应数量的旧文件被封存(如上所述,地下室已满;他们需要腾出空间才能存储任何来自仓库的更多文件)。
巴斯特不了解或关心整个仓储业务的细节;那是梅根的工作。所有他都知道的是,通常情况下,他手中存档的请求单很快就得到了处理,但有时他们需要花费数小时。
回到原来的问题
那么,整个精心制作的练习有什么意义呢?简而言之,建立一个比不透明的“魔法缓存”更具体的模型,使我们能够更清楚地思考所涉及的物流。在设计芯片时,物流与运营高效办公室一样重要。
最初的问题是“为什么我们不构建一个大型缓存,而不是几个小缓存”。因此,如果您说四核机器具有4个核心,每个核心具有32KB L1数据高速缓存,256KB二级高速缓存以及2MB共享三级高速缓存,那么为什么我们不能开始使用~3MB共享高速缓存?
在我们的比喻中:出于同样的原因,我们的办公桌面宽度可能达到1.50米,而不是在一个宽150米的巨大办公桌上安置四个不同的人。
在桌面上放置东西的意义在于它可以轻松到达。如果我们把办公桌弄得太大,我们就会失去这个目的:如果我们需要走50米才能得到我们想要的文件,那么它在技术上“就在我们的办公桌上”并不是真的有帮助。 L1缓存也是如此!使它们变大使它们(物理上)更大。除此之外,这使得访问速度变慢并消耗更多能量(出于各种原因)。 L1缓存的大小非常大,足以使用,但足够小,因此它们仍然可以快速访问。
第二点是L1缓存处理不同类型的访问,而不是缓存层次结构中的其他级别。首先,有几个:有L1数据缓存,但也有一个L1指令缓存,例如英特尔酷睿CPU还有另一个指令高速缓存,即uOp高速缓存,它(根据您的观点)是并行L1指令高速缓存或“L0指令高速缓存”。
要求L1数据高速缓存读取和写入大多数通常在1到8个字节之间的单个项目,稍微更少(对于SIMD指令)。层次结构中较高的缓存级别通常不会受到单个字节的影响。在我们的办公室类比中,某些不在桌面上的所有内容只是以与缓存行相对应的单个文件(或更大)的粒度来处理。内存子系统也是如此。当核心正在执行内存访问时,您将处理单个字节;较高级别的缓存通常处理数据批量,一次处理一个缓存行。
L1指令高速缓存具有与数据高速缓存完全不同的访问模式,并且与L1数据高速缓存不同,就核心而言,它们是只读的。 (写入指令高速缓存通常是间接发生的,方法是将数据放在一个更高级别的统一高速缓存中,然后让指令高速缓存从那里重新加载它们的数据)。出于这些(和其他)原因,指令高速缓存通常与数据高速缓存完全不同;使用单个统一的L1缓存意味着最终的设计需要满足几个相互冲突的设计标准,从而导致妥协,使其在任何目的上都变得更糟。统一的L1缓存还需要处理指令和数据流量,这是相当大的负载!
另外一点:作为一名程序员,很容易忽略获取指令需要多少缓存带宽,但这是相当多的。例如,当不从uOp高速缓存运行代码时,所有英特尔酷睿i7 CPU内核可以在每个周期从L1指令高速缓存中获取16字节的指令,并且只要指令执行跟上,就会保持这样做。解码。在3GHz,我们在这里讨论的是每个内核50GB / s的顺序,仅用于指令提取 - 但是,只有当指令获取单元一直处于忙碌时才被授权,而不是由于某种原因而被停顿。实际上,L2缓存通常只看到其中的一小部分,因为L1指令缓存工作得很好。但是,如果您正在设计统一的L1缓存,则需要至少预测高指令和高数据流量的突发(想想类似于L1数据缓存中包含源和目标的几千字节数据的快速memcpy) 。
顺便说一下,这是一个普遍的观点。 CPU核心可以处理每个周期的许多内存访问,只要它们都在L1缓存中命中。对于3GHz的“Haswell”或更高版本的Core i7,如果你有恰当的指令组合,我们说的是每个核心的聚合代码+数据L1带宽超过300GB / s;在实践中不太可能,但有时候你仍然会得到很热的活动。
L1高速缓存旨在尽可能快地处理这些突发活动。只有错过L1需要传递到更高的缓存级别,这不需要几乎同样快,也没有那么多的带宽。他们可以更担心功率效率和密度。
第三点:分享。在办公室类比或每个核心L1缓存中使用单独的办公桌的一个重要方面是它们是私有的。如果它在你的私人办公桌上,你不需要问任何人;你可以抓住它。
这很关键。在办公室比喻中,如果你与4个人共用一张巨大的桌子,你不能只抓一个文件。这不仅仅是你的桌面,而你的其他3位同事之一现在可能需要该文件(也许他们正试图将它与他们刚刚在桌子另一端拾取的另一个文件交叉引用!)。每次你想要拿东西时,你都需要喊出“如果我抓住这个,那么每个人都好吗?”,如果其他人想要它,你必须等待。或者你可以有一些方案,每个人都需要抢票并排队等候,如果发生冲突就轮到他们。或者是其他东西;这里的细节并不重要,但你做的任何事都需要与他人协调。
多个核共享缓存也是如此。你不能只是在未经宣布的情况下开始踩踏数据;您在共享缓存中执行的任何操作都需要与您共享的所有其他内容进行协调。
这就是我们拥有私有L1缓存的原因。 L1缓存是您的“桌面”。当你坐在那里时,你可以继续工作。 L2缓存(“文件柜”)处理与其他人的大部分协调。大多数时候,工作人员(CPU核心)坐在办公桌前。 Buster可以过来,拿出一份新请求列表,并将先前请求的文件放入文件柜,而不会中断工作人员。
只有当工人和Buster想要同时访问文件柜时,或者当其他人请求放在工作人员桌面上的文件时,他们才需要停下来互相交谈。
简而言之,L1缓存的工作是首先为其CPU核心服务。因为它是私有的,所以它需要很少的协调。 L2缓存仍然是CPU核心的私有缓存,但除了缓存之外,它还有一个额外的责任:处理大多数总线流量和通信而不是中断核心(这有更好的事情要做)。
L3缓存是共享资源,需要全局协调对它的访问。在办公室类比中,工作人员只有一种方式来访问它:即通过巴斯特(巴士)。公共汽车是一个阻塞点;希望前两个缓存级别已经将内存访问次数降低到足够远,以至于不会成为性能瓶颈。
注意事项
本文介绍了一种与当前桌面(和笔记本)x86 CPU匹配的特定缓存拓扑:每核拆分L1I / L1D缓存,每核统一L2缓存,共享统一L3缓存,核心通过环形总线连接。
并非每个系统都是这样的。一些(主要是较旧的)系统没有拆分指令和数据缓存;有些人拥有完整的哈佛架构,将指令和数据存储器完全分开。 L2通常在多个核心之间共享(认为一个办公室有一个文件柜和多个办公桌)。在这种类型的配置中,L2高速缓存有效地充当核之间的总线的一部分。许多系统没有L3缓存,有些系统同时具有L3和L4缓存!我也没有谈论有多个CPU插槽的系统等。
我坚持使用环形总线,因为它与这个类比非常吻合。环形巴士相当普遍。有时(特别是当只需要连接两个或三个块时)它是一个完整的网格;有时是多个环形公交车与一个横梁相连(它合理地映射到办公室类比:一个多层办公大楼,其中一个“巴斯特”每层楼都有他的轮,还有一个连接楼层的电梯)。
作为软件开发人员,有一种自然的倾向,即假设您可以将模块A神奇地连接到模块B,并且数据只是从一端传送到另一端。内存实际工作的方式到现在为止非常复杂,但提供给程序员的抽象只是一个庞大而扁平的字节数组之一。
硬件不能那样工作。碎片不是通过看不见的以太魔法连接。模块A和B不是抽象概念;它们是物理设备,实际的微型机器,占据硅片上的实际物理区域。芯片有一个“平面图”,这不是模糊的点头或开玩笑;这是一张实际的2D地图。如果要将A连接到B,则需要在它们之间运行实际的物理连线。电线占用空间,驱动它们需要电力(越多越长)。在A和B之间运行一堆电线意味着它可以用来连接其他东西的物理阻塞区域(是的,芯片在多个层上使用电线,但它仍然是一个严重的问题;如果您感兴趣,谷歌“路由拥堵”) 。在芯片上移动数据是一个实际的后勤问题,而且是一个非常复杂的问题。
因此,虽然办公室的事情是诙谐的,“谁需要与谁交谈”和“这个系统的几何形状看起来如何 - 它是否承认合理的布局?”是硬件和整体系统设计的非常相关的问题这会产生巨大的影响。空间隐喻是概念化潜在现实的有用方式。