在大多数处理器中,为什么L1缓存的大小小于L2缓存的大小?

时间:2011-01-12 08:41:21

标签: caching memory cpu-architecture processor cpu-cache

为什么L1缓存的大小小于大多数处理器中L2缓存的大小?

7 个答案:

答案 0 :(得分:43)

L1与CPU内核紧密耦合,可在每次访问内存时访问(非常频繁)。因此,它需要非常快速地返回数据(通常在时钟周期内)。延迟和吞吐量(带宽)对L1数据缓存都是性能关键。 (例如,四个周期延迟,并且每个时钟周期支持CPU核心的两次读取和一次写入)。它需要大量的读/写端口来支持这种高访问带宽。使用这些属性构建大型缓存是不可能的。因此,设计师保持小,例如目前大多数处理器都是32KB。

仅在L1未命中时才访问L2,因此访问频率较低(通常是L1的1/20)。因此,L2可以具有更高的等待时间(例如,从10到20个周期)并且具有更少的端口。这使设计师能够做得更大。


L1和L2扮演着截然不同的角色。如果L1变得更大,它将增加L1访问延迟,这将大大降低性能,因为它将使所有依赖负载变得更慢并且更难以隐藏无序执行。 L1大小几乎没有争议。

如果我们删除了L2,L1未命中将不得不进入下一级别,比如内存。这意味着很多访问将进入内存,这意味着我们需要更多的内存带宽,这已经是一个瓶颈。因此,保持L2周围是有利的。

专家经常将L1称为延迟过滤器(因为它使L1命中的常见情况更快)和L2作为带宽过滤器,因为它减少了内存带宽使用。

注意:我在我的论证中假设了一个2级cache hierarchy,以使其更简单。在今天的许多多核芯片中,所有内核之间共享一个L3缓存,而每个内核都有自己的私有L1和L2。在这些芯片中,共享的最后一级缓存(L3)扮演着内存带宽过滤器的角色。 L2扮演片上带宽滤波器的角色,即它减少了对片上互连和L3的访问。这允许设计人员使用较低带宽的互连,如环形和慢速单端口L3,这样可以使L3更大。

或许值得一提的是端口数量是一个非常重要的设计点,因为它会影响缓存消耗的芯片面积。端口为缓存添加了线路,这消耗了大量的芯片面积和功率。

答案 1 :(得分:29)

这有不同的原因。

L2存在于系统中以加速存在L1高速缓存未命中的情况。如果L1的大小与L2的大小相同或大于L2,则L2不能容纳比L1更多的高速缓存行,并且不能处理L1高速缓存未命中。从设计/成本的角度来看,L1缓存绑定到处理器并且比L2更快。缓存的全部思想是,通过添加比最慢的硬件更高性能(且更昂贵)的中间硬件,并且比您拥有的更快的硬件更便宜,可以加快对较慢硬件的访问速度。即使您决定将L1缓存加倍,您也会增加L2,以加速L1缓存未命中。

那为什么会有L2缓存呢?好吧,L1缓存通常更高性能且构建成本更高,并且它必然绑定到单个核心。这意味着将L1大小增加一个固定数量将使该成本在双核处理器中乘以4,或在四核中乘以8。 L2通常由不同的内核共享 - 取决于架构,它可以在处理器中的几个或所有内核之间共享,因此即使L1和L2的价格相同,增加L2的成本也会更低 - 其中它不是。

答案 2 :(得分:18)

@Aater's answer explains some of the basics。我将在英特尔Haswell和AMD Piledriver上添加更多细节+真实缓存组织的示例,具有延迟和其他属性,而不仅仅是大小。

有关IvyBridge的一些详细信息,请参阅my answer on "How can cache be that fast?",并讨论整体负载使用延迟,包括地址计算时间,以及不同级别缓存之间数据总线的宽度。

L1需要非常快速(延迟和吞吐量),即使这意味着有限的命中率。 L1d还需要在几乎所有体系结构上支持single-byte stores,并且(在某些设计中)支持未对齐访问。这使得很难使用ECC(纠错码)来保护数据,实际上一些L1d设计(英特尔)只使用奇偶校验,只有在外部缓存级别(L2 / L3)才能使用ECC,可以完成ECC在较大的块上以降低开销。

设计一个单一级别的缓存是不可能的,它可以提供现代multi-level cache 的低平均请求延迟(平均所有命中和未命中)。由于现代系统有多个非常饥饿的核心,所有核心都与同一个相对较高延迟的DRAM共享,这一点至关重要。

每个核心都需要自己的私有L1来提高速度,但至少最后一级缓存通常是共享的,因此从多个线程读取相同数据的多线程程序不必转到DRAM它在每个核心上。 (并充当由一个核心写入并由另一个核心读取的数据的后备)。 这需要至少两级缓存才能实现理智的多核系统,并且是当前设计中超过2个级别的动机的一部分。现代多核x86 CPU在每个内核中都有一个快速的2级缓存,并且所有内核共享一个更慢的缓存。

L1命中率仍然非常重要,因此L1缓存并不像它们那样小/简单/快速,因为这会降低命中率。因此,实现相同的整体性能需要更高级别的缓存更快。如果更高级别处理更多流量,则它们的延迟是平均延迟的更大组成部分,并且它们更频繁地阻塞其吞吐量(或者需要更高的吞吐量)。

高吞吐量通常意味着能够在每个周期(即多个端口)处理多个读取和写入。对于与低吞吐量缓存相同的容量,这需要更多区域和功率,这是L1保持较小的另一个原因。

L1也使用速度技巧,如果它更大则不会起作用。即大多数设计使用Virtually-Indexed, Physically Tagged (VIPT) L1,但所有索引位都来自页面偏移量以下,因此它们的行为类似于PIPT(因为虚拟地址的低位与物理地址中的相同)。这避免了synonyms / homonyms(错误命中或缓存中的相同数据两次,并且看到Paul Clayton关于链接问题的详细答案),但仍然允许部分命中/未命中检查与TLB查找。 VIVT缓存不必等待TLB,但必须在对页表的每次更改时失效。

在x86(使用4kiB虚拟内存页面)上,32kiB 8路关联L1缓存在现代设计中很常见。可以基于虚拟地址的低12位来获取8个标签,因为这些位在虚拟和物理地址中是相同的(它们低于4kiB页面的页面偏移)。这种L1缓存的速度攻击只有在它们足够小并足够关联以使索引不依赖于TLB结果时才有效。 32kiB / 64B线/ 8路相关性= 64(2 ^ 6)组。因此,一行中最低6位的地址选择字节,接下来的6位索引一组8个标记。这组8个标签与TLB查找并行获取,因此可以与TLB结果的物理页选择位并行检查标签,以确定缓存的8种方式中的哪些(如果有的话)保存数据

制作更大的L1缓存意味着它必须等待TLB结果才能开始获取标签并将它们加载到并行比较器中,否则它必须增加关联性以保持log2(集合)+ log2 (line_size)< = 12.(更多关联性意味着每组更多的方式=>更少的总组数=更少的索引位)。所以例如一个64kiB的缓存需要是16路关联的:仍然是64套,但是每套都有两倍的方式。这使得增加L1尺寸超出当前尺寸在功率方面非常昂贵,甚至可能甚至延迟。

在L1D缓存逻辑上花费更多的功率预算会为乱序执行,解码,当然还有L2缓存等提供更少的功率。让整个内核运行在4GHz并且每个时钟维持~4个指令(在高ILP代码上)而不需要熔化需要平衡设计。请参阅此文章:Modern Microprocessors: A 90-Minute Guide!

缓存越大,刷新它就会损失得越多,因此大型VIVT L1缓存会比当前的VIPT更糟糕 - 就像PIPT一样。并且更大但更高延迟的L1D可能也会更糟。

According to @PaulClayton,L1缓存通常会在与标记并行的集合中获取所有数据,因此一旦检测到正确的标记,就可以选择它。这样做的功耗成本与关联性成比例,因此一个大的高关联L1对于功耗和芯片区域(和延迟)非常不利。 (与L2和L3相比,它不会占用很多区域,但物理接近度对延迟很重要。当时钟周期为1/4纳秒时,光速传播延迟很重要。)

较慢的缓存(如L3)可以以较低的电压/时钟速度运行,以减少热量。它们甚至可以为每个存储单元使用不同的晶体管布置,以使存储器的功率更优化而不是高速。

多级缓存有很多与电源相关的原因。功率/热量是现代CPU设计中最重要的限制因素之一,因为冷却微小的芯片很难。一切都是速度和功率(和/或芯片面积)之间的权衡。此外,许多CPU由电池供电或处于需要额外冷却的数据中心。

L1几乎总是分成单独的指令和数据缓存。我们可以将单独的L1I缓存绑定到单独的I而不是统一L1中的额外读取端口来支持代码获取。 -TLB。 (现代CPU通常具有L2-TLB,其是用于由L1 I-TLB和D-TLB共享的翻译的第二级高速缓存,而不是由常规L2高速缓存使用的TLB)。这为我们提供了64kiB的L1缓存,静态分区为代码和数据缓存,比具有相同总吞吐量的怪物64k L1统一缓存便宜得多(并且可能更低的延迟)。由于代码和数据之间通常很少重叠,这是一个很大的胜利。

L1I可以放置在物理上接近代码获取逻辑,而L1D可以物理上靠近加载/存储单元。当时钟周期仅持续1/3纳秒时,光速传输线延迟是一个大问题。布线布线也很重要:例如Intel Broadwell has 13 layers of copper above the silicon

拆分L1对速度有很大帮助,但统一的L2是最佳选择。 有些工作负载的代码非常小,但触摸了大量数据。将更高级别的高速缓存统一以适应不同的工作负载是有意义的,而不是静态地划分为代码与数据。 (例如,几乎所有的L2都将缓存数据,而不是代码,同时运行一个大的矩阵乘法,而不是在运行膨胀的C ++程序时有很多代码热,甚至是复杂算法的有效实现(例如运行gcc) )。代码可以作为数据复制,而不是总是通过DMA从磁盘加载到内存中。

缓存还需要逻辑来跟踪未完成的未命中(因为无序执行意味着在解决第一个未命中之前可以继续生成新请求)。有许多未命中意味着您重叠未命中的延迟,实现更高的吞吐量。在L2中复制代码和数据之间的逻辑和/或静态分区并不好。

较大的低流量缓存也是放置预取逻辑的好地方。硬件预取可以在诸如循环数组之类的事情上实现良好的性能,而不需要每一段代码都需要软件预取指令。 (SW预取很长一段时间很重要,但HW预取程序比以前更聪明,因此Ulrich Drepper's otherwise excellent What Every Programmer Should Know About Memory中的建议对于许多用例而言已经过时了。)

低流量的高级别缓存可以提供延迟,可以执行诸如使用自适应替换策略等智能内容,而不是通常的LRU。 Intel IvyBridge and later CPUs do this,以抵制对于工作集没有缓存命中的访问模式,这些访问模式只是略微过大而无法容纳在缓存中。 (例如,在同一方向上循环一些数据两次意味着它可能会在重复使用之前被逐出。)

一个真实的例子:英特尔Haswell 。来源:David Kanter's microarchitecture analysisAgner Fog's testing results (microarch pdf)。另请参阅英特尔的优化手册(标签wiki中的链接)。

另外,我写了一个单独的答案:Which cache mapping technique is used in intel core i7 processor?

现代英特尔设计使用由所有核心as a backstop for cache-coherence traffic共享的大型包容性L3缓存。它在核心之间物理分布,每核2048套* 16路(2MiB)(an adaptive replacement policy in IvyBridge and later)。

较低级别的缓存是每个核心。

  • L1 :每个核心32kiB每个指令和数据(拆分),8路关联。 延迟= 4个周期。至少2个读端口+ 1个写端口。 (Maybe even more ports to handle traffic between L1 and L2,或者可能从L2接收缓存行与退出商店冲突。)可以跟踪10个未完成的缓存未命中(10个填充缓冲区)。
  • L2 :统一的每核256kiB,8路关联。 延迟= 11或12个周期。读带宽:64字节/周期。主要的预取逻辑预取到L2。可以跟踪16个未完成的失误。每个周期可以为L1I或L1D提供64B。实际端口数未知。
  • L3 :统一,共享(由所有核心)8MiB(用于四核i7)。包含(所有L2和L1每个核心的缓存)。 12或16路关联。 延迟= 34个周期。充当缓存一致性的后盾,因此修改后的共享数据不必去主内存并返回。

另一个真实的例子:AMD Piledriver :(例如Opteron和桌面FX CPU。)缓存线大小仍然是64B,就像英特尔和AMD已经使用了几年一样。文本主要是从Agner Fog的microarch pdf,with additional info from some slides I found复制而来,还有关于直写L1 + 4k写入组合缓存on Agner's blog的更多细节,comment that only L1 is WT, not L2

  • L1I :64位,双向,在一对核心之间共享(AMD的SMD版本比超线程具有更多的静态分区,并且每个核心称为核心。对共享一个向量/ FPU单元和其他管道资源。)
  • L1D :每个核心16个,4路。 延迟= 3-4 c 。 (请注意,页面偏移下方的所有12位仍然用于索引,因此通常的VIPT技巧有效。)(吞吐量:每个时钟有两个操作,其中一个是存储)。 Policy = Write-Through,带有4k写入组合缓存。
  • L2 :2 MB,16路,在两个核心之间共享。 延迟= 20个时钟。每4个时钟读取吞吐量1。每12个时钟写入1个吞吐量。
  • L3 :0 - 8 MB,64路,在所有核心之间共享。 延迟= 87时钟。每15个时钟读取吞吐量1。写入吞吐量1每21个时钟

Agner Fog报告说,当一对核心都处于活动状态时,L1吞吐量低于一对闲置时的另一半。不知道发生了什么,因为对于每个核心,L1缓存应该是分开的。

答案 3 :(得分:3)

对于对此类问题感兴趣的人,我的大学建议Computer Architecture: A Quantitative ApproachComputer Organization and Design: The Hardware/Software Interface。当然,如果您没有时间,可以在Wikipedia上获得快速概述。

答案 4 :(得分:2)

我认为首先出现这个问题的主要原因是,L1-Cache速度更快,因此更昂贵。

答案 5 :(得分:1)

这里的其他答案给出了具体和技术上的原因,为什么L1和L2的大小与它们一样,虽然其中许多是特定架构的激励因素,但它们并非真正必要:潜在的架构压力导致增加当您离开核心时,(私有)缓存大小相当普遍,并且与首先出现多个缓存的原因相同。

三个基本事实是:

  1. 大多数应用程序的内存访问都表现出高度的时间局部性,并且分布不均匀。
  2. 在各种流程和设计中,缓存大小和缓存速度(延迟和吞吐量)可以相互抵消 1
  3. 每个不同级别的缓存都涉及增量设计和性能成本。
  4. 因此,在基本级别,您可能会说缓存的大小加倍,但与较小的缓存相比,会导致1.4的延迟惩罚。

    因此它成为一个优化问题:你应该拥有多少个缓存,它们应该有多大?如果内存访问在工作集大小内是完全一致的,那么你最终可能会得到一个相当大的缓存,或者根本就没有缓存。但是,访问非常不均匀,因此小而快的缓存可以捕获大量访问,与其大小不成比例。

    如果事实2不存在,那么您只需在芯片的其他约束内创建一个非常大,非常快的L1缓存,而不需要任何其他缓存级别。

    如果事实3不存在,那么你最终会得到大量细粒度的高速缓存"中心更快更小,外面更慢更大,或者可能是具有可变访问时间的单个缓存:对于最靠近核心的部分更快。实际上,规则3意味着每个级别的缓存都有额外的成本,因此您通常会得到一些量化级别的缓存 2

    其他限制

    这提供了一个了解缓存计数和缓存大小决策的基本框架,但也存在次要因素。例如,Intel x86具有4K页面大小,其L1缓存使用VIPT架构。 VIPT意味着缓存的大小除以路数不能比4 KiB大 3 。因此,在半打英特尔设计中使用的8路L1缓存最多可以是4 KiB * 8 = 32 KiB。这可能与那些设计上的L1缓存的大小完全相同并非巧合!如果不是这种约束,那么你完全有可能看到较低的相关性和/或较大的L1缓存(例如,64 KiB,4路)。

    1 当然,在权衡中也存在其他因素,例如面积和功率,但保持这些因素不变,适用大小 - 速度权衡,即使不保持不变,基本行为是一样的。

    2 除了这种压力之外,像大多数L1设计一样,已知延迟缓存有一个调度优势:无序调度器可以乐观地提交依赖于内存负载的操作在L1缓存将返回的周期中,从旁路网络读取结果。这样可以减少争用,并可能减少关键路径上的延迟周期。这会对最里面的缓存级别施加一些压力,使其具有统一/可预测的延迟,并可能导致缓存级别降低。

    3 原则上,您可以使用没有此限制的VIPT缓存,但只能通过要求OS支持(例如,页面着色)或其他约束。 x86 arch还没有完成,现在可能无法启动。

答案 6 :(得分:-2)

从逻辑上讲,这个问题会自行解决。

如果L1大于L2(组合),那么就不需要L2缓存。

如果可以将所有内容存储在硬盘上,为什么要将这些内容存储在磁带机上?