Chapter1 WHAT IS A CACHE MEMORY?

1.1 CPU SPEED VS. SYSTEM SPEED
问题很简单。设计师们不断努力以最具成本效益的方式发挥他们设计的最大潜力。当某个特定CPU的更快版本面世时,设计师通常会尝试通过简单地增加CPU时钟频率来提高现有设计的吞吐量。
在某个点之后,系统的主存储器(有时称为后备存储器)的速度成为系统吞吐量的限制因素。这在图1.1中有所体现。X轴表示CPU时钟频率,Y轴表示系统的总吞吐量。对于这个例子,我们假设系统是计算受限的;也就是说,系统的性能受到CPU性能的限制,而不是输入/输出(I/O)设备(例如磁盘驱动器)的速度。在计算受限的系统中,CPU缓存非常重要,因为它们仅用于提高与CPU相关操作的效率。

我们还假设,从CPU的角度来看,系统主存储器的性能在CPU时钟频率范围内是相同的。换句话说,如果主存储器的平均访问时间为60ns,这个访问时间将不会因CPU速度而改变。(即使在本文写作时,60ns这个数字也不算是特别快的速度,更何况几年后。作者要求读者在阅读文本时插入任何适当的数字,并预计问题将保持不变。)这个常数主存储器访问时间假设的价值将在本章1.2节中进行辩护。
在图表的早期部分,吞吐量与CPU时钟速度成比例地线性增加,直到标记为A的峰值。这很直观。突然间,它降到了一个更低的水平。为什么会这样?在时钟频率高达约20 MHz之前,处理器可以自由地尽可能快地运行,而没有来自60ns主存储器的等待周期。一旦处理器需要小于60ns的访问时间,必须插入等待周期来说明差异。单个等待周期足以满足,直到时钟频率需要小于60ns的访问时间,即使只有一个等待周期也是如此,这被标记为点B。再次,系统的吞吐量突然下降,就像添加第一个等待周期时一样。正如图表所示,增加时钟频率仅仅将系统的吞吐量通过Figure 1.1中的锯齿状图案表示的狭窄波段,而没有任何提高性能的希望,这个波段的上限由内存访问时间设定。更糟糕的是,锯齿状图的峰值和低谷趋势随着系统时钟频率的增加而减小,这是我不确定是否完全可以解释的现象。
当然,通过使用快速主存储器可以解决这个问题。仅剩的问题是,要支持最快的操作,必须使用什么速度的主存储器?
1.2 THE COST OF FAST MAIN MEMORY
快速的主存储器价格昂贵。一般规律是,一个快速存储芯片与下一个速度等级之间的价格差异可以高达50%或100%!所有这些只为了获得额外20%的速度!如果这个世界是一个公平的地方,在动态随机存取存储器(DRAM)上获得20%的速度增长应该支持CPU在速度上的20%增长。然而,实际发生的事情远非如此。大多数系统在CPU和DRAM之间插入一些缓冲区,另外在DRAM数据输出与CPU数据输入引脚之间插入另外一些缓冲区(Figure 1.2)。

//DMA Device和Main Memory不是一个东西?
/*
DMA设备(直接内存访问设备)和主存储器是不同的东西。
DMA设备是一种特殊的硬件设备,它可以绕过CPU,直接与主存储器(DRAM)进行数据传输。它可以读取或写入主存储器中的数据,而无需CPU的干预。常见的DMA设备包括网络适配器、硬盘控制器、图形卡等。DMA设备通常用于高速数据传输,以提高系统性能。
主存储器是计算机系统中用于存储程序和数据的物理存储器。它是CPU可以直接读取和写入的内存空间。主存储器存储着正在运行的程序、处理过程中的数据和其他临时存储的数据。
虽然DMA设备可以访问主存储器,但它们并不是同一个东西。DMA设备是用于数据传输的特殊硬件设备,而主存储器则是存储程序和数据的物理存储空间。
*/
//DMA device对应的DMA Controller是软件还是硬件?
/*
DMA(Direct Memory Access)是一种计算机系统中的技术,它允许数据不通过CPU直接在内存和外设之间传输。DMA设备是一个专用硬件,它负责管理和控制数据传输,但是,DMA设备本身并不足以完成数据传输,需要有一个DMA控制器协同工作来完成数据传输任务。
DMA控制器通常是一个独立的芯片或模块,它可以是硬件实现,也可以是软件实现。在硬件实现中,DMA控制器是一个物理设备,它与CPU和其他外设相连,可以通过寄存器或总线接口进行配置和控制。而在软件实现中,DMA控制器是由操作系统内核或相关驱动程序实现的,它通过编程方式控制DMA设备的行为,在内核层面管理DMA传输。
总的来说,DMA设备和DMA控制器都是硬件实现的,但是DMA控制器可以是硬件或者软件实现的。在硬件实现中,DMA控制器通常是一个单独的芯片或模块,而在软件实现中,则由操作系统内核或相关驱动程序负责实现。
*/
这些缓冲器不仅增加了CPU驱动系统中许多DRAM地址输入所需的小电流,还在另一个设备需要控制主存储器的那些时候,将CPU与内存隔离开来。这些设备的传输延迟是固定的,也就是说,这些设备的传输延迟并不像DRAM和静态RAM(以后我们将称之为SRAM)的存储时间那样随着半导体工艺的进步急剧下降,也不如微处理器的时钟速度增加得快。其他静态时间是写周期数据和地址设置和保持时间(相对于写脉冲边缘),这些时间通常对任何速度级别的DRAM或SRAM都是相同的。一个典型的SRAM可能有约5ns的写数据设置时间和1ns的保持时间。问题在于,尽管制造商试图在其更快速的零件规格中减小这些数字,但改进远不成比例地等于读取访问时间的任何改进。因此,在整个CPU时钟周期时间中被这些静态时间消耗的比例越来越大。在缩小的时钟周期内,如果去除了这些静态设置和保持时间,则为了满足CPU的需要,用于内存访问的时间必须比时钟周期缩短得多。

图1.3展示了一个无缓存处理器系统的时钟频率与内存子系统所需访问时间之间的关系,以支持该系统上的零等待读取周期。对于此示例,使用了图1.2所示的系统,基于386处理器,因为该处理器没有内部缓存,所有缓冲区提供5ns的传播延迟,并且系统中没有由引脚电容或PCB负载引起的其他延迟(这些将在第2章2.5节中详细介绍)。为了公平起见,我包含了386的两种工作模式,流水线和非流水线。对于任一情况,趋势渐近地接近1ns的访问时间,但流水线操作的计时在更高的时钟频率下达到零。
1.3 THE CONCEPT OF LOCALITY
我们都是非常务实的设计师。当被要求设计一个符合特定目标规格的系统时,我们试图以最低合理的成本来实现规格要求。前面两节中提出的论点似乎表明,设计一个能够利用当前可用最快CPU速度的系统将变得代价过高。
这就是统计学派上场的地方。在20世纪60年代,IBM的研究人员发现几乎所有代码都具有极高的重复性,这一事实可以用于提高计算机吞吐量的优势。如果任何重复性的内容都可以存储在小型、高速的存储器中,那么等待状态的影响只会限制在程序中较少重复的部分,而这些部分可以存储在更慢、更便宜的存储器中。
程序的哪个部分是重复的?这个问题通过局部性原理来回答,包括空间局部性和时间局部性。
1.3.1 Locality in Space
空间局部性或引用局部性是指大多数计算机代码在一个小区域内重复执行的现象。这个空间不一定在主存储器的单个地址范围内,而可以相当分散地分布。

为了说明,图1.4展示了一个汇编代码程序。这个程序并没有以任何严格的方式编写,但足以展示空间局部性的一般概念。该程序是所有计算机中都存在的代码类型的简化版本。首先,一个循环被用来遍历存储在内存中的一个99个字符的字符串。其次,一个子程序包含了一段可能对主程序的其他部分有用的代码。在进入和退出子程序时,栈被用来临时存储和检索调用程序的程序计数器(PC)。这段代码的功能是扫描一个字符串,找到大写字母"A"并将该字符串中所有出现的大写字母"A"替换为小写字母"z"。
第一个非常明显的事实是,代码被编写成在处理器通过循环时反复使用相同的一组指令,主要是子程序内部的指令。空间局部性原则指的是调用程序、子程序和堆栈空间都存在于从地址00001000(调用程序)、00011008(子程序)和F0000007开始的三个非常狭窄的区域内。
//一个程序的代码段对应的物理地址应该是连续的(so有自己的连续物理地址空间),申请的动态内存对应的物理地址范围是?局部变量对应的栈地址范围是?
1.3.2 Locality in Time
第二个局部性原则,时间局部性或时间引用,仅仅指的是这个例子中的指令按照接近顺序执行,而不是在时间上被分散。处理器更有可能需要访问它10个周期之前访问过的内存位置,而不是10,000个周期之前访问过的位置(在166 MHz Pentium上只有0.12毫秒)。这似乎非常明显,但如果没有空间和时间的局部性,缓存将无法工作。
结合空间和时间的局部性,可以简单地说明,在这个例子中,程序将在执行相同的几条指令99次后花费一段时间,然后转移到另一个代码片段,这个代码片段可能也会被重复。如果读者查看一个真正的程序,一个比这里使用的示例更长的程序,将会发现与这个示例大小相似的重复序列位于更大的重复循环内,而这些循环又位于更大的循环内,如此循环下去。
利用时间和空间的局部性是个好主意,可以确保程序的重复部分在使用时从非常快的内存执行,并在等待使用时驻留在较慢、成本较低的内存中。乍一看,最明显的实现方法是将内存空间分为快速和较慢的部分,并让操作系统根据需要将代码的部分从较慢的部分复制到较快的部分。这正是虚拟内存系统的工作原理,其中较慢的内存是磁盘等大容量媒体,而较快的部分是主内存或DRAM。另一种方法是除了完整的较慢主内存之外,还拥有一块小而快速的内存,并使用专门的硬件来确保主内存中当前有用的部分被复制到这块小而快速的内存中。这就是我们所说的缓存内存。
基于软件的技术在内存映射方面非常优秀、经济实惠,并且在内存映射方面运作良好,但它并不适用于缓存管理,原因有两点。首先,成本考虑通常会使缓存非常小,通过软件将内存空间映射进出缓存会消耗过多时间,从而抵消了缓存的速度增益。(通常需要一个至少与我们在图1.4中示例循环的大小相当的软件来将示例循环移入快速内存,而小而快速的内存则需要相对频繁地替换其内容。)其次,大部分缓存被设计用于加速已有的系统,在这些系统中,操作系统和其他软件无法重新配置以支持分割为快慢部分的内存。在这种系统中,软件必须对缓存的存在一无所知。所有现有程序在缓存系统上都必须以类似但更快的方式运行。实现这一目标的缓存称为软件透明缓存。
1.4 FOOLING THE CPU
因此,设计一个内存控制器,在硬件层面上在慢速和快速内存之间来回传输数据,让CPU始终认为正在访问相同的地址,只是有时候这个位置的访问速度比其他时候快。虽然以这种方式欺骗CPU听起来很棘手,但实际上并不是。一旦读者理解了基本概念,简单的缓存设计几乎变得微不足道。

我喜欢用一个同事曾经告诉我的类比来解释,就像是一个文员的桌子旁边放着大量的文件柜,可能是在一个非计算机化的信用调查机构(见图1.5)。文员的平凡工作是接听电话并根据来电者的查询从信用申请人的档案中阅读条目。文件按照申请人的电话号码进行存储(为了方便论述,我们采用美国风格的七位数电话号码)。在一个典型的工作日里,文员会接听50到100个电话,并会从桌子旁边站起来检索文件50到100次。然而,由于同一个信用申请人可能在同一天或两天内在五到十家银行申请贷款,其中几个电话的请求数据与前一个电话或几天前的请求数据相同。(敏锐的读者可能会注意到,这个类比使用了时间局部性,但忽略了空间局部性。)
现在假设文员非常聪明,他看到办公桌的膝洞里还有一个未使用的文件抽屉。为什么不在桌子的文件抽屉中保留那些当前需求较高的文件的副本呢?复印机就在文件柜旁边,所以复制一份回办公桌上没有问题。但谁知道哪些文件会被重复请求,哪些文件只会被请求一次呢?无法预测未来,所以文员会简单地将每个从文件柜中取出的文件都复制一份。
这开始非常容易。当需要某个文件时,文员将其副本放入办公桌的文件抽屉,并在挂在墙上的一张纸上记录这个事实。这张纸是办公桌文件抽屉的目录。接听电话时,文员首先查看目录,确定办公桌文件抽屉是否有所需文件的副本。如果有,文员不需要站起来,只需从抽屉中取出该文件即可节省时间和精力。目录还列出了在文件抽屉中可以找到某个特定信息的位置。在无法在文件抽屉中找到文件副本的情况下,文员将站起来,从文件柜中复制文件,并在目录上做出相应的记录。
一切都运作得很好,直到文件抽屉变满了。那时怎么办?文员非常实际,决定在每个目录条目旁边加上最后访问日期和时间的记录。当新文件放入办公桌文件抽屉时,具有最旧日期和时间的目录条目将被擦除,文件将被丢弃,并将新的文件和目录条目放在相同位置。(在另一个有趣的类比中,Intel曾将同样的问题比作去超市买东西但更喜欢将经常使用的物品存放在家里的冰箱里。)

这就是缓存内存设计的概念。庞大的文件柜银行代表系统的主内存,而文员则代表CPU。办公桌文件抽屉称为缓存数据内存或数据RAM,目录恰如其分地称为缓存目录。刚刚描述的确切实现很少使用,因为硬件要求比一些非常好的替代方案更昂贵。在确切的缓存实现中使用了几种技巧,本书的相当一部分内容将用于介绍这些技巧。
缓存内存有四个基本部分,如图1.6所示。 缓存数据内存是小型、快速的内存,用于存储指令和数据的副本,这些指令和数据从主内存访问时速度较慢。当讨论缓存的大小时,缓存大小等于仅缓存数据内存中字节的数量。目录不包括在此数字中。缓存目录是相应位置的缓存数据内存中存储的数据的主存储器地址列表(即,如果某个缓存数据位置中的数据来自主存储器地址0000 0000,则缓存目录将包含0000 0000的表示)。因此,每个缓存位置都存储数据,同时也存储地址,使得组合的目录和数据RAM表现得像单个非常宽的内存。
总线缓冲区具有非常重要的功能,尽管它们基本上与图1.2中显示的非缓存系统中使用的芯片相同。这些缓冲区现在被控制的方式是,如果缓存可以提供主存储器位置的副本(这称为缓存命中),则不允许主存储器将其数据放入CPU的数据引脚上。在许多缓存设计中,除非缓存已指示其不包含CPU请求的数据的副本(缓存未命中或有时为故障),否则永远不会发送地址到主存储器。因此,在所有缓存命中周期中,CPU与缓存通信,在所有缓存未命中中,CPU与主存储器通信。 CPU、缓存数据RAM和系统总线缓冲区之间的数据总线有时被称为缓存的数据路径。
整个系统的关键是缓存的控制逻辑,也称为缓存控制器或缓存管理逻辑。这个逻辑实现了将数据从缓存数据内存和缓存目录中移入和移出的算法。这是缓存设计的关键所在,缓存逻辑体现了一些备受争议的决策,被称为缓存策略。一旦决定并实施了某种策略,它就成为了缓存的政策。在第二章中,我们将详细讨论这些策略。控制逻辑还确定何时打开和关闭总线缓冲区,并确定何时从缓存数据RAM读取和写入。在一些系统中,系统总线接口足够复杂,以至于缓存控制器的系统总线端被细分为其自身的一部分,这时它可能被称为存储控制单元(SeE)。
缓存大小和策略都会影响缓存的命中率(CPU周期中缓存命中的百分比)。相反,未命中率是CPU周期的剩余百分比。基于每个总线事务的平均等待周期数(像是访问DDR的cpu cycle),可以计算缓存系统的吞吐量。该数字等于未命中率乘以系统总线上的平均等待周期数(再加上命中率乘以缓存延迟时间,如果缓存需要使用等待周期)。
举个例子,假设所有的系统总线访问都需要三个等待周期,而缓存在零等待周期内响应。对于一个命中率为90%的缓存内存(很容易实现),缓存系统的平均等待周期数将会是 10% * 3 = 0.3 个等待周期。在这种计算中,小数位的等待周期是完全有效的,因为这是等待周期和零等待周期之间的平均值。同样的系统如果使用更小的缓存,可能只能达到80%的典型命中率,那么平均等待周期数将是较大缓存的两倍,即0.6个等待周期。如果使用速度更慢的DRAM,需要更多的等待周期才能完成总线周期,那么吞吐量的提升会更加显著。
另一种有趣的思考方式是将问题扩展到所有系统时钟周期的消耗。假设我们有一个处理器,其平均零等待指令执行时间为两个周期,而缓存未命中需要三个等待周期,总共需要 3 + 2 = 5 个周期。在一个命中率为80%的缓存中,系统中的十条指令将消耗 10 * 0.8 * 2 + 10 * (1 - 0.8) * (2 + 3) = 26 个周期,其中只有16个周期能够从缓存中执行。换句话说,CPU将尝试处理20%的指令却花费了将近40%的时间。这也意味着总线将在40%的时间内被占用,对此我们将在第1.8节进行探讨。
缓存的工作过程遵循一定的模式。在上电时,缓存包含随机数据,并不允许对CPU的请求做出响应。当处理器从主存储器中读取数据时,缓存数据RAM会被命令复制该位置的内容,而相应的缓存目录位置被告知复制CPU请求的地址。这个过程对任何后续周期也将发生,直到遇到一个循环为止。一旦处理器到达循环的末尾,它将再次输出第一个位置的地址,这很可能仍然存在于缓存数据内存中。然而,这一次,该位置的数据将从缓存中提供给处理器,而不是从主存储器中提供,与循环中的其他指令一样。这显然会有多大的帮助。在我们的示例程序中,循环中的指令只有在第一轮中以主存储器速度运行,然后接下来的98次循环中将更快地从缓存中执行。
这涵盖了缓存读取周期。在写入周期中发生的情况取决于所选择的缓存策略,并将在第二章中进行深入探讨。从CPU的角度来看,数据始终来自主存储器,尽管有时候它会比其他时间到达得更快。
1.5 CACHE DATA AND CACHE-TAG MEMORIES
现在我们知道了如何简单地将CPU的主存储器请求重新定向到缓存来满足,但是缓存目录和缓存数据内存的设计还没有解释。对于初学者来说,这可能会很吓人。目录体系结构的选择对缓存数据内存的设计有很大的影响,因此首先将对目录进行研究。
某些大学级课程解释了缓存目录由内容可寻址存储器(Content Addressable Memory,CAM)组成。这是一种目录类型,与我们在信用局中的职员的例子非常相符。CAM是一种反向存储器,当数据被提供给特定输入时,它输出一个地址。该地址显示在CAM内找到匹配条目的位置。所有CAM位置同时进行匹配数据的检查,如果找到匹配项,其地址将被放置在地址输出引脚上。当需要将32位处理器地址转换为较小的缓存地址时,这特别有用。32位处理器地址被呈现到CAM的数据输入引脚,并且一个较短的地址从地址输出引脚中弹出。事实上,几乎从不使用CAM来实现缓存设计,但它们便于解释缓存目录,因此我们将首先研究CAM,然后再看看替代方案。

从中有一个重要的问题是“什么是CAM?”CAM由一系列地址寄存器组成,每个寄存器都包含一个比较器,用于将寄存器中包含的地址与当前在比较地址总线上的地址进行比较(见图1.7)。这通过为每个地址寄存器附加一个单独的比较器来实现。
尽管听起来需要大量硬件,但实际上可以使用专门的七晶体管静态RAM单元在硅中相对容易地制造,其复杂度不到工业标准四晶体管单元的两倍。比较器输出被简单的优先编码器编码为匹配条目地址的二进制表示。在写入周期期间,CAM会同时接收地址和数据,并生成写脉冲。然而,在读取周期期间,数据输入到CAM中,并输出地址。输出地址是包含匹配数据的CAM位置的地址。如果没有找到匹配数据,则CAM输出无匹配信号。
读者可能会问“为什么CAM并不普遍可得?”简单的原因是,比CAM方法表现几乎相同的更简单方案可以使用标准的静态RAM构建,并且可以使用更便宜的缓存数据RAM。这些替代方法将在此简要讨论,并在第2章中深入探讨。缓存设计师不愿意为CAM付出与CAM吸引半导体制造商兴趣所需的成本一样多。作者只知道市场上有两种CAM可用。第一种是一种成熟产品,一种ECL 4 x 4位设备,本来将被设计用于大型机缓存,但后来速度更快(70ns)的静态RAM可用。另一种是专门为局域网地址过滤器设计的设备,在相对较长的时间内(即10ns)需要输入48位比较地址的三个复用块。

回到例子中:假设系统刚刚初始化,缓存位置中没有包含主存位置的有效副本。在缓存中应该放置处理器访问的第一个位置?有些处理器会在复位后从第一个主存地址或地址零开始寻找其第一条指令。其他处理器从内存空间的顶部(FFFF FFFF)开始。还有一些处理器在这两个地址中寻找一个向量,然后跳转到该向量指向的地址。
如果缓存目录是一个CAM,那么任何主存地址都可以映射到任何目录位置。这种方法称为全关联缓存,这个术语简单地意味着任何主存地址都可以在缓存内的任何位置复制。用于确定哪个缓存位置应该用于存储主存地址副本的方法称为映射或散列(我们将在本书中研究一些散列算法)。每当要将某物放入缓存时,将不得已(希望未使用)的目录位置分配给它。缓存控制器通过将表示要用于存储输入数据的目录位置的地址分配给CAM和缓存数据内存来执行此操作,在访问主内存时进行。同时,将从主内存访问的地址(完全独立于刚提到的目录地址)中包含的数据写入目录,同时将主内存数据馈送到CPU并写入缓存数据RAM(见图1.8)。稍后,当处理器的地址再次与CAM中存储的地址匹配时,CAM输出包含匹配数据的缓存数据RAM位置的地址。该地址被馈送至缓存数据RAM,其由标准静态RAM组成,该地址中的数据可供CPU使用。
由于大型CAM的缺乏,人们设计了几种巧妙的CAM方法替代方案,这些方案也往往比CAM方法要简单得多。回到档案员的比喻上来,我们可以设计一个组织系统,利用抽屉的尺寸与所有文件柜中的抽屉尺寸相同的事实。使用的电话号码都是七位数字,并遵循格式867-5309。此外,假设文件柜是按前缀(电话号码的前三个数字)排列的,每个抽屉可以携带该前缀范围内所有10,000个号码或信用申请的文件(从 -0000 到 -9999)。因此,第一抽屉将具有000-0000至000-9999等数字。文员还可以将桌子抽屉分成10,000个插槽,以便每个插槽只允许包含与插槽号匹配的后缀(最后四位数字)的文件,换句话说,号码867-5309只能放在桌子抽屉插槽5309中。目录仍然是墙上的纸,其中包含10,000个条目,描述存储在每个桌子抽屉插槽中的数据的电话号码;但是,每个条目都位于与电话号码的后缀匹配的位置,因此只需要将前缀写入目录中(因此对于号码867-5309,将前缀867写入第5309行)。此外,文员无需再选择放置新缓存条目的位置,因此目录条目不再需要包含其上次访问的时间。这是今天缓存设计中最流行的散列算法,称为集合关联缓存(组相联缓存),因为为了清晰起见,电话号码的最后四位数字已被命名为缓存的集合地址,在任何集合内部,条目都是关联的(可以有任何前缀)。
通过这种新方法,文员可以在查阅目录的同时开始查看桌子抽屉中的文件。不再需要交叉参考目录以确定哪个桌子抽屉位置包含该文件。如果正确的集合地址(5309)上的前缀(867)与来电者请求的号码匹配,那么文员几乎可以立即从桌子抽屉插槽5309中取出文件。在集合关联设计中,处理器输出的地址被分割成前缀和后缀的等效部分,这个位置是由缓存的大小和架构确定的。低地址位,即与示例中电话号码的最后四位数字对应的位,被称为集合位,因为它们包含集合地址。有些人将其称为索引位,但这个术语与虚拟内存转换中使用的术语相冲突,为了清晰起见,在本文中将不使用这个术语。剩下的(高位)与所选集合的目录条目进行比较,被称为标记位。
你可能还记得我在最初描述文员类比时提到过的一句话,即这个类比没有考虑到局部性。这很容易理解,因为文员随时可能接到任何文件的电话,代表任何电话号码,以任何顺序。计算机代码从不以这种随机的方式运行,而往往充满了循环和重复,并且只要程序计数器不在循环或子程序调用上跳来跳去,代码就会按顺序执行。集合关联方法利用了这种空间局部性,将顺序指令放置在不完全随机的缓存位置上,而是在连续的位置上。正如刚刚演示的那样,集合关联缓存不允许将主存地址映射到任何缓存地址,而是限制缓存,使得缓存位置的低地址位必须与匹配的主存地址的低地址位相匹配。由于处理器将遍历一小组连续地址,这看起来应该没有问题,而且集合关联缓存应该与更复杂的全关联设计一样高效。但在第1.6节中我们会看到情况并非如此。

前面的段落提到,完全关联缓存(具有CAM)对比集合关联缓存需要使用更昂贵的缓存数据RAM(因为性能原因,需要选择更快的RAM)。为什么呢?在完全关联缓存中,所有地址位在输入缓存数据RAM之前都要通过目录进行过滤。这意味着在缓存CPU的最快周期——缓存读命中时,处理器地址必须首先经过目录CAM并转换为缓存地址,然后该地址必须通过缓存数据RAM才能将读取数据提供给CPU(参见图1.9a)。这意味着处理器输出地址与数据从缓存返回之间的延迟至少等于CAM和RAM的传播延迟之和。因此,如果需要在处理器内返回数据,比如说30纳秒以内,那么这30纳秒必须被分配给CAM和RAM。现在的150 MHz微处理器要求这个过程在不到20纳秒内完成。拥有8纳秒的CAM(祝你好运!)和8纳秒的缓存数据RAM的系统将难以在这个关键路径上使用任何逻辑。
关联集高速缓存将CAM(内容地址存储器)和RAM(随机存取存储器)的延迟减少为单个RAM访问。在1.9b图中,CPU地址直接连接到缓存目录和缓存数据RAM。为了简化设计,所示的设计使用与缓存数据RAM相同深度的标准静态RAM作为缓存目录。(关于此的其他选择将在第2章中详细解释。)假设缓存深度为8,128(8K),需要13位地址才能完全访问。然后,较低的13位CPU地址同时路由到缓存目录和缓存数据RAM。当缓存数据RAM找到与匹配的较低地址位(但不确定哪些上位地址位)存储在主存储器位置上的数据副本时,目录则查看存储在具有相同较低地址位的位置上的副本的上位地址位。这很令人困惑,但基于桌子类比,请记住电话号码867-5309的最后四位是5309,因此在文件抽屉中访问槽5309。然后,目录必须告诉我们是否存在存储的前缀(867)与正在访问的其他前缀号码(984-5309)是否匹配。为了解决延迟问题,要注意在处理器周期内同时检查缓存目录和缓存数据RAM,缓存目录的输出只是用来告诉CPU是否以全速(命中)继续运行,还是等待主存访问来获取CPU请求但不在缓存中的数据。
//上面讲的很清晰!

关联集高速缓存中的目录比全相联高速缓存中的目录简单得多。全相联高速缓存的目录不仅需要跟踪是否存在一个有效条目,该条目包含在缓存中的主存储器位置(这个事实尚未描述,但将在第2章中描述),还需要跟踪该条目在缓存数据RAM中的位置,而关联集目录只需要查看具有与CPU输出地址相同集位的缓存条目是否来自具有相同标记位的位置,并且该位置是否有效。这是一个简单的比较。位置5309上的标记是否与电话号码867-5309的867匹配,并且位置是否有效?显然,可以使用异或门输入到与门进行标记比较,这是一个简单地址比较器芯片的功能(图1.10)。有效性同样简单,也将在第2章中详细描述。在处理器的读取周期中,关联集高速缓存假设(除非另有说明)缓存中的数据就是处理器正在请求的数据。缓存数据RAM启动读取周期时,将其数据输出打开,并且其地址引脚直接与处理器的较低有效地址输出引脚连接(参见图1.9b)。同时,缓存标记RAM正在查找地址的更高位,以查看缓存条目是否来自正确的地址。如果所请求的地址与主存储器数据的缓存副本的地址匹配,则缓存控制器向处理器发出准备信号以接收来自缓存数据RAM的数据,并允许继续进行。如果缓存中具有与某一特定集地址相同的来自主存不同部分的数据(即,标记位不匹配),则缓存控制器通过不断言准备输入来声明发生了缓存未命中,然后将主存储器数据读入缓存,允许CPU继续进行,同时主存储器数据和地址被写入CPU。关联集高速缓存中的缓存数据RAM只需具有略小于处理器最小地址到数据周期的访问时间。缓存标记RAM的访问时间也会稍慢一些,减去比较器和下游逻辑所需的延迟。较慢的静态RAM通常比较快的静态RAM便宜得多,因此设计者可以通过采用关联集设计节省很多费用。一些静态RAM制造商提供的产品中包含在标准SRAM内部的比较器。这样做在速度、组件数和下游逻辑复杂性方面具有某种优势。无论目录是由一个简单的内部包含比较器的RAM还是具有两者的芯片制成,关联集高速缓存中的目录通常称为缓存标记存储器,因为它是存储地址标记的位置。还有其他几个术语用于描述缓存标记RAM,特别是标记RAM、缓存地址比较器、地址比较器、缓存比较器或简称比较器。
1.6 THRASHING: GETTING THE MOST OUT OF THE CACHE
//thrashing:抢占
在前面的部分中讨论的整个论点忽略了一种称为“thrashing”的现象。当缓存 thrashes 时,一个常用位置被另一个常用位置所替代。每当 CPU 在缓存中找不到所需内容时,它必须以主存储器较慢的速度执行主存储器访问操作。这使得这些访问至少与没有缓存时一样慢。
假设处理器必须按照紧密的序列检查两个地址,这些地址包含相同的集合位。这是如何发生的?有几种方式。首先,大多数代码都由调用例程和子例程构建。子例程中的某些代码可能具有与调用例程中的代码相同的集合位,因为调用例程可能与子例程无关。对于集合位较少的小缓存来说,这尤其正确,这使得冲突的可能性更高。接下来,考虑调用子例程时会发生什么。程序计数器和可能还有很多其他寄存器被推入堆栈。堆栈在哪里?堆栈的集合位与调用例程和子例程中的代码的集合位匹配的可能性有多大?集合位可能会混淆的另一个地方是当程序使用指针时,比如移动值块或检查文本字符串。最后,考虑当中断被服务时会发生什么。中断服务例程(或由该服务例程调用的某个子例程)是否具有与调用例程地址的集合位匹配的集合位?这些任何一种情况都可能导致缓存 thrash。

//汇编指令JSR什么意思
//使用 JSR 指令时,程序会将当前的执行地址保存在栈中,并跳转到指定的子程序开始执行。子程序完成后,通过返回指令(通常是 RTS 或 RET)返回到原调用位置继续执行。
例如,让我们重新审视图 1.4 中所示的代码片段。三个地址具有 008 十六进制的最后三位数字:调用例程中循环的开头,堆栈指针和子例程的开头。如果我们的缓存使用这三个最后三位数字作为集合地址,或者使用这三个数字的子集,则缓存将 thrash。下一个段落和图 1.11 将详细说明这一点。在这种情况下,堆栈指针是一种“陷阱”,因为它会导致 CPU 覆盖缓存中的其他项。我之所以指出这一点,是因为很多设计师倾向于忘记推送和弹出项目的指令会执行读取和写入操作,您必须考虑才能记住这些操作。对于缓存设计师来说,总是知道堆栈指针的位置是一种豪迈的表现,尤其是在多任务操作系统下。
首先,缓存加载地址0000 1000,即“加载堆栈指针”的指令。它被放置在缓存位置000中,并在标记位置000处写入标记地址0000 1。所有这些都发生在主存储器周期时间内,因此缓存不会加速这个特定的周期。类似地,接下来的两个指令也以主存储器访问时间加载到缓存地址004和008中,并且这两个新缓存副本的标记也被填充为在标记地址004和008处的0000 1。位于地址0000 1008的指令是一个“跳转到子程序”(JSR),因此程序计数器被推入堆栈位置F000 0008,并且在这个例子中的缓存设计中,将立即覆盖存储在内存地址0000 1008(JSR指令)的指令,再次以主存储器访问的较慢速度进行。进入子例程后,从地址0001 1008获取子例程的第一条指令,再次以主存储器访问时间获取,并立即替换存储在缓存位置008处的程序计数器的新缓存副本。当遇到返回指令时,推入的程序计数器不是从缓存中恢复,而是从主内存恢复,因为它被子例程的第一条指令从缓存中弹出。对于此堆栈地址,没有实现缓存速度优势。最后,重新进入调用循环,并再次从主存储器获取地址0000 1008处的JSR指令,而不是从缓存中获取,再次以较慢的主存储器周期时间进行。这三个项目在缓存中不断替换彼此的整个过程重复了99次。显然,与008匹配的较低地址位的位置根本没有从缓存的使用中受益!(其他地址(除了00C)缓存收益了)
这个例子的主要观点是,小型缓存中频繁发生的抖动,在更大的缓存中逐渐不再重要,因为位设置数量增加,导致地址之间的位设置混淆减少。你可能想考虑两个极端的论证:在只有一个位设置(地址的最低有效位)的系统中,抖动有多大可能性?在具有与 CPU 地址输出数量相同的位设置(缓存大小与最大可用主存储器一样大)的系统中,抖动可能性有多大?第二个观点是,抖动是集合关联缓存的现象,其中的缓存行根据其位设置而不是基于某种更合理的算法进行覆盖写入。全关联缓存将会像处理没有任何地址位匹配的事务一样轻松处理刚才演示的问题。在第2章中,我们将探讨介于集合关联和全关联模型之间的缓存体系结构。与抖动相关的另一个术语是必然失效。无论缓存设计有多好,都会发生一些缓存失效,简单地因为缓存被要求提供此前没有包含的数据。由于无法避免这些失效,它们被称为必然失效。抖动从不导致必然失效。然而,和本书中的大多数术语一样,还有一些替代术语可以用来描述必然失效,例如稳定失效和非冲突失效。
1.7 CACHES AND THE MEMORY HIERARCHY
内存层次结构或存储层次结构指的是内存的价格、大小和性能在不同阶段的变化。计算机中最昂贵的内存是处理器内部的寄存器。这些设备被设计成执行非常快速,并且它们的结构允许在一个寄存器被写入CPU的同时,CPU同时读取一个或多个不同的寄存器。该结构每个位需要消耗多个晶体管,并且成本和速度与每个位所需的晶体管数量有关。寄存器的访问是通过片上总线进行的,因此寄存器数据的访问时间不受离开芯片的信号要求的限制。大多数处理器仅具有少量寄存器,或者只有几十个寄存器,这些寄存器的宽度等于芯片内的数据路径。寄存器的访问时间通常低于2纳秒。
在大多数非缓存系统中,下一级内存是主存储器。主存储器通常由动态随机存取存储器(DRAM)组成,其访问时间是寄存器访问时间的几倍。动态随机存取存储器使用每个位一个晶体管的方式制造,支持电路需要额外的少量晶体管。DRAM的结构使其成为最便宜的半导体存储器形式。在当代基于微处理器的系统中,典型的DRAM主存储器容量可能在16到512兆字节之间。目前,主存访问时间大约为70纳秒。
下面的两个级别由旋转存储介质组成。磁盘通常用于存储无法保存在主存储器中的数据。如今的硬磁盘访问时间在几十毫秒左右,容量从几百兆字节到数千兆字节不等。硬盘通常与可拆卸介质备份,这些介质通常是磁带或软盘。这两种介质都比硬盘便宜得多,但访问时间需要几秒钟。由于这个级别是可拆卸的,所以其大小只受用户保留磁带或软盘的意愿限制。
缓存内存位于上述两个级别之间。现今的一些微处理器包含一个内部缓存,由标准的静态RAM位构成。与寄存器不同,这些位置不能同时写入和读取,因此在芯片上的缓存中进行某些操作时,会更慢一些。一个寄存器位可能需要十个或更多的晶体管来实现,而用于内部缓存的SRAM单元可以包含每位4到6个晶体管。由于微处理器使用相同的芯片制造,芯片上的缓存大小受到限制,主要是为了避免处理器的成本飙升。芯片成本与芯片大小并不成比例,而是以二次以上的速率增加。芯片上缓存共享不需要离片访问的优点,因此它们非常快速。现今的微处理器通常包含8到64K字节的缓存。
在具有芯片缓存和没有芯片缓存的系统中都使用外部缓存。连接到未缓存的处理器的外部缓存用于加速主存储器访问,如上所述。这样的缓存通常从256K字节到16M字节不等,使用经济、由每位4个晶体管构成的SRAM芯片制造的四志存器单元内存单元。缓存的访问时间大约为3到15纳秒。如果处理器具有芯片缓存,则通常会使用与芯片缓存架构相差很大的体系结构来实现任何外部缓存,这是为了降低成本,并弥补芯片缓存的一些不足之处。这些设计问题将在第2章中进行探讨。

表1.1以美元规模和带宽显示了一个高性能缓存系统的典型存储层次结构,其处理器使用芯片上的缓存。
1.8 REDUCING BUS TRAFFIC
在本章中尚未提到的添加缓存到系统中的一个原因是:多处理器可以利用缓存内存来提高其主存储器的有效总线带宽。总线带宽,也称为总线传输或简称流量,是以每秒字节数衡量的在总线上移动数据的最大速度。例如,一个字宽为16位(2字节)的总线,可以每秒传输高达800万个字,其带宽为2 x 8 = 16兆字节/秒。由处理器使用的总线带宽百分比被称为利用率。(类似于主存储器带宽,缓存到CPU接口有自己的带宽,称为缓存带宽。如果缓存带宽低于CPU所需的带宽,CPU必须通过降低时钟频率或添加等待状态来减慢速度。)

我们将很快看到,使用缓存内存来减少处理器对主存储器带宽的需求是一个非常重要的好处,并且随着多处理器体系结构作为显著提高系统性能的手段日益被广泛接受,这个好处将变得更加重要。如果多个处理器的利用率接近100%,它们可能会遇到麻烦,并在达到100%限制后性能不如较少处理器的系统。
存在两种多处理器系统,如图1.12a和b所示。顶部示意图显示了称为松散耦合多处理器系统或分布式存储多处理器(DMM)的系统。松散耦合系统实际上是两个或多个不同的处理器,每个处理器都能够独立运行。在最广义的意义上,通过调制解调器或网络连接的任何两台个人电脑都可以被称为松散耦合多处理器系统;然而,该术语更常用于更密切连接的系统,例如通过先进先出(FIFO)缓冲区、双口RAM或专用串行总线进行连接。大多数这样的系统在运行时之前将任务划分得很好,以使每个处理器可以在任务的专用部分上达到其最佳能力。处理器之间的类型通常是不同的(例如,数字信号处理器和通用处理器)。
在本书中我们将深入讨论的多处理器系统类型被称为紧密耦合多处理器(图1.12b)。在紧密耦合系统或共享内存机器(SMM)中,通过系统总线,两个或更多处理器访问单个主内存。任务分配是由软件在运行时进行的,可以完全灵活地运行各种应用程序。目前,紧密耦合架构主要用于文件服务器、超小型计算机和大型机,但当前的趋势表明它们可能成为下一个世纪普遍采用的架构。
紧密耦合系统需要缓存的原因并不难理解。在紧密耦合系统中,每个处理器都有自己的缓存(图1.12b)。缓存的访问频率越高,处理器使用总线访问主内存的次数就越少。这有助于防止总线饱和,即降低利用率,使得总线及时对那些需要的处理器更加可用。
缓存处理器所需的总线带宽与缓存的缺失率成正比,因此设计多处理器系统的设计者比单处理器系统的设计者更注重缺失率。单处理器系统的设计者可能会犹豫是否要花费额外的资金将缓存的命中率从96%提高到98%。这对性能测试的影响几乎可以忽略不计。然而,在多处理器系统中,命中率从96%提高到98%意味着缺失率从4%降低到2%,即减少一半。换句话说,一个设计所需的带宽是另一个设计的一半,因此在饱和效应开始显现之前,可以使用系统中只有一半数量的处理器。
让我们以更直观的方式来分析多处理器系统中加入处理器的情况会发生什么。我们的理想是获得与加入系统的处理器数量成比例的增量性能增加。一个五处理器系统应该以单处理器系统的五倍或接近于五倍的性能运行。为了这个论点,让我们假设示例中使用的处理器每条指令需要两个时钟周期,所有指令需要单个存储器访问(一个不真实的低数字),并且主存储器的延迟时间为零。

在单处理器系统中,使用了一半的主存带宽,并且我们可以实现一个百分之百的标准化性能水平。如果我们添加另一个处理器,并且这两个处理器交错,它们将消耗整个总线带宽,并且将以单处理器系统的200%的速度执行。在这样的系统中添加第三个处理器将不会增加任何东西,因为它没有交错插槽,并且没有内存带宽可以服务于第三个处理器。这个系统的性能与处理器数量的关系如图1.13所示。
这种情况过于简化了,但它为更现实的问题奠定了基础。通常,复杂指令集计算机(CISC)的指令需要可变数量的周期,并且每个指令使用多个主存访问。这立即排除了交错的有效性,因为它需要平衡和可预测才能有用。在实际系统中,总线仲裁被用于允许所有处理器根据需要访问总线。仅仅使用仲裁机制会减慢速度,因为在分配前处理器必须请求总线。这意味着主存访问直到处理器尝试启动访问一次仲裁延迟后才能开始。仲裁的效果是单处理器不再能够以上面示例中的100%的水平执行,并且添加处理器对系统的整体性能有一个更渐进而不那么有用的影响。

在图1.14a中,我们看到当处理器被添加时,仲裁多处理器系统的操作方式。添加处理器增加了同时仲裁请求的概率,所以添加处理器的性能增加并不像第一个例子中那么显著,当接近总线饱和时,性能会有更加渐进式的下降。在这样的系统中,当达到饱和时,添加处理器实际上会减慢整个系统的速度。从这个角度来看,如果每个添加的处理器需要恰好20%的可用总线带宽(一个不真实的低水平),那么一个六处理器系统将需要120%的可用带宽,将以100%/120%的速度运行。高性能处理器实际上需要非常接近100%的总线带宽,所以问题比这个例子显示的更糟。
在我们的例子中仍然使用零等待主存储器。当我们为这个维度添加一些现实情况时会发生什么呢?很容易看出,我们可以将通常是单周期主存访问加入一个等待状态,并将总线带宽减半,因此在前面的段落中使用的100%数字降至50%。每个处理器现在只能以其潜力的50%/120%操作。六处理器系统的标准化吞吐量降至6 x 50%/120%,或单处理器与零等待主存储器系统的吞吐量的2.5倍。如果每个处理器的总线利用率被提高到更真实的80%水平,那么六处理器系统的性能将为6 x 50%/480%,导致组合系统吞吐量只有单个零等待处理器的63%!因此,在实际世界中,未缓存的紧密耦合多处理器不是提高系统吞吐量的一种具有成本效益的方法。当我们添加缓存时会发生什么呢(图1.14b)?回到交错示例。假设我们拥有与第一个示例中相同的零等待状态系统,并且每个处理器都有一个非常适度的90%总体命中率的高速缓存,或者是10%的缺失率。突然间,50%的总线使用要求降至50%的10%,即5%。这意味着总线现在不会饱和,直到安装了20个处理器!多么不同啊!当然,这都是在一个非常理想的环境中进行的,在这个环境中,处理器只需要50%的总线带宽,主存储器没有等待状态。我们刚才看到,等待状态对统计数据产生全新的影响,大多数处理器超过80%的时间需要其总线。向多处理器系统添加缓存的一个不幸的副作用是,设计者现在必须考虑所有可能的新问题,因为两个缓存可能包含相同的数据,并且这些多个副本不会自动匹配。这是一个非常困难的问题,整个第四章都致力于解决它!简言之,向多处理器系统添加高速缓存的好处足以使人们了解多处理器缓存协议的令人昏昏欲睡的练习。我曾经看到一种奇怪的扭曲方式,用于减少多处理器系统中的总线流量,它涉及由动态RAM构建的缓存。缓存越大,错失率越低。如果流量是您的问题,则错失率是您的解决方案。DRAM每美元给您提供的内存是SRAM的四倍,但速度较慢。这个缓存的设计者非常关心总线流量,而不是单个处理器的性能,因此选择制作一个更大的、虽然速度较慢的DRAM缓存,而不是制作一个速度更快的SRAM缓存。
1.9 REDUCING POWER CONSUMPTION
最近出现了一种使用缓存存储器的新理由,这已成为一些争论的主题。有人认为可以将缓存存储器添加到低功耗系统(如笔记本电脑)中,从而降低整体功耗。反对者立即指出,实现缓存设计通常使用的快速存储器和逻辑的功耗很高。如何在不增加功耗的情况下将它们添加到系统中?此外,他们经常引用统计数据,显示笔记本电脑的主要功耗点是平板显示器的背光和硬盘驱动器,它们共同消耗了系统功耗的三分之二以上。
支持者认为,缓存降低了主存储器的访问次数,通常使其几乎等于允许的最小刷新周期的数量。这本身大大降低了DRAM的功耗,相比每个存储器周期都访问DRAM时的功耗。该功耗减少几乎抵消了相同数量的SRAM的功耗。此外,如果任何时候访问的DRAM数量大于为缓存打开的SRAM数量,则处理器从缓存运行时的功耗必然低于从主内存运行时的功耗。最后,CPU在其关闭周期之间遭受的较少等待状态,CPU的整体功耗将降低。
//所以,若使得cache miss率降低后,要注意CPU+DRAM的功耗减少量和SRAM的功耗增加量
关于这一点,还没有定论。我个人怀疑,缓存作为节能设备的优势如果存在,也很小,并且高度依赖软件。想象一下,一个系统制造商自豪地宣称某个型号的电池续航时间在运行Lotus 1-2-3时通常为三小时,但在运行Microsoft Word时可以达到五个小时。
在某些手持系统中,处理器的内部缓存可以与定制软件结合使用以降低功耗。首先,将软件划分成模块,以适应缓存的大小,使整个模块在需要时驻留在缓存中。当该模块被加载到缓存中时,主存储器(有时为ROM或其他非易失性存储器)以正常功耗运行。当该模块在缓存中执行时,主存储器不需要供电,因此可以进入降低功耗模式,或完全关闭。通过利用缓存,可以降低功耗,并且如果软件经过适当调整以最小化缓存访问,则可以将功耗最小化。
1.10 AN EXAMPLE CACHE
图1.15展示了一个示例缓存,以确保你明白缓存设计并不难。该缓存是为68020设计的一款旧缓存,它是一款32位的摩托罗拉处理器,没有内置缓存。该缓存使用离散逻辑而不是可编程逻辑设计,这对我们来说是个优势,因为这样设计更容易理解。

图1.15a展示了两个存储器阵列,左边是缓存标签RAM,右边是缓存数据RAM。使用更现代的部件,整个缓存数据RAM可以仅用四分之一的32Kx32 SRAM芯片实现。缓存标签RAM使用了集成复位可重置的缓存标签SRAM芯片,这些设备将在第二章详细介绍。从根本上讲,这些芯片与标准的8Kx8芯片之间几乎没有区别。

//cache controller
//包含:HIT/MISS LOGIC, CACHE ENABLE/DISABLE,WRITE PULSE GENERATION
移动到图1.15b,我们可以看到实现整个缓存所需的所有逻辑。这包括六个存储元素、16个双输入门和五个反相器。地址解码器U10仅用于将地址映射为软件缓存复位命令。缓存策略的名称实际上比这些逻辑少得多,但可能更令人生畏。这个缓存是一个直接映射的写通透统一逻辑缓存。如你所见,名称比逻辑本身更复杂。
尽管逻辑看起来很简单,但不要被误导!有很多方法一开始看起来非常简单,只需要很少的逻辑就可以实现,但它们与系统的其他部分存在许多相互依赖关系,使得问题很快变得复杂,让许多设计者无法在第一时间完成缓存设计。Chips & Technologies在M/PAX芯片组中由于缓存策略的延迟问题受到了重创,而在英特尔将数百万个奔腾处理器交付之前,他们才解决了多处理缓存协议中的所有缺陷。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值