Chapter5 INTERESTING CACHE TRICKS

每个缓存设计师都会在某个时候遇到由缓存设计方法引起的困难。可能是因为缓存无法包含原始设计中指定的所有最优雅的目标,或者可能存在实际约束,需要在一个领域使用离常规的策略,而现在在另一个领域存在逻辑上的后果。无论出于何种原因,所有设计师都知道几乎可以找到解决这类困境的答案,但通常需要以不同的方式思考问题。
本章专门介绍了一些被各种处理器设计师用于改善特定情况下系统性能的技巧。在某些情况下,设计师利用系统划分来提高性能,在其他情况下,他们解决了由于选择了其他方法而引起的问题。这里展示的所有技巧都有点超出传统范畴,并且出于这个原因,我决定它们值得额外关注。
5.1 EFFICIENTLY FEEDING A SUPERSCALAR MACHINE

作者遇到的最有趣的缓存之一是IBM为Power处理器的早期版本RS/6000设计的。这个超标量CPU有四个处理器,每个处理器可以从一个指令缓存中获取数据。如图5.1所示,指令缓存行从四个缓存中一次取出四个字,每个缓存包含主存中的每第四个字。这四个字将被输入到一种交叉点矩阵中(指令缓冲区/多路复用器网络),它会将每个指令路由到适当的处理器。
优化编译器在将适当的数据加载到缓存中起着重要作用,每个处理器加载四个相邻的字,用于每个处理器的有用指令;然而,很明显,一旦编译器遇到少于四个处理器同时工作的区域,某个处理器将不需要指令,这将导致某些缓存位置根本不被使用。这不是一个最优的情况。事实上,如果这是情况的话,处理器可以进一步解耦,每个处理器都有自己的缓存和自己的指令获取地址寄存器,并且在一定程度上独立地处理指令。超标量体系结构的一个优点是它们始终保持良好的同步,而在这里描述的更松散耦合的系统将失去这个优势。
相反,IBM的设计师决定加载四个缓存,并通过交叉点开关轮流为各个处理器提供指令。这样每个处理器看起来都有自己的指令缓存,尽管实际上采用了更高效的共享缓存方法。
那么,他们如何确保不必浪费宝贵的缓存位置来存放缺失的指令呢?首先,编译器被赋予了尽可能紧密地将指令打包到内存中的自由。如果下一步只需要三个处理器,那么只会将三条指令插入到连续的三个位置中。下一个指令集将从第四个位置开始加载,而不是第五个位置。当编译器只生成一或两条指令时,同样的情况也适用。
不幸的是,指令获取地址寄存器以四的增量移动,表示四个缓存。如何将指令获取地址寄存器与指令边界同步?这是设计中的有趣部分。

答案就在指令获取地址寄存器和每个缓存的地址输入之间放置的增量器中。这些是图5.1中标有"N+1"的小方框和下游多路复用器。通过这些增量器,指令获取地址寄存器的输出可以被看作是指向当前行,或者下一行的地址。例如,假设指令恰好以三个一组的形式出现,如图5.2所示(我们将其称为三元组)。"Xn"是针对处理器X的指令,"Yn"是针对处理器Y的指令,依此类推。为了简单起见,我们假设处理器W在连续多个指令中处于空闲状态,而X、Y和Z每个周期都接收到新的指令。我们也忽略了使用缓存中的多个路(Way)的问题,只考虑左边缓存的情况。图5.2a显示了主存如何存储这些指令,然后图5.2b展示了它们如何加载到缓存中。现在的关键是以一种合理的方式从缓存中取出指令并发送到四个处理器中。
首先,指令获取地址寄存器指向每个缓存的第一个地址。前三个缓存提供这些指令,然后通过交叉点开关将它们路由到适当的处理器:指令缓存0提供给处理器X,以此类推。在获取这些指令之后,指令获取地址寄存器不会被更新,而是将指令缓存3中的第一个地址的指令通过交叉点开关发送到处理器X,并且所有三个增量器都将指令获取地址寄存器的增加输出定向,以便从指令缓存0、1和2的第二个地址输出到交叉点开关,从而允许指令缓存0提供给处理器Y,指令缓存1提供给处理器Z。这样,三元组不必对齐到四条指令的边界,缓存内存位置能够高效利用。
至于RS/6000的数据缓存有多复杂,可以不必担心。它是一个简单的四路组相联的虚拟写回缓存,每行大小为128字节。并发的行写回由读缓冲和写缓冲管理。
尽管缓存比虚拟地址页长度更深,但通过使用规则来避免数据缓存别名问题。这个规则是,通常会将两个虚拟地址位翻译成不同的物理地址,但这两个虚拟地址位必须与物理地址位完全匹配。重叠的组地址和页位始终保持相同。这是由软件管理的,这是在系统设计中同时控制硬件和软件的好处之一。
5.2 PRIMARY AND SECONDARY CACHES ON THE SAME CHIP
许多系统设计师认为,一级缓存必然是“芯片内部”,而二级缓存必然是“芯片外部”。这与事实相去甚远。在第2章中提到了一个使处理器芯片配备两级缓存的很好的理由-小缓存需要更简单的地址解码逻辑,这可能使得使用小型一级缓存有助于提高芯片的整体运行频率。设计师现在给出的另一个理由是,如今庞大的芯片尺寸需要很长时间来穿越,所以在设计中散布若干小型缓存可能会以更高的时钟频率执行,而在类似的设计中使用一个大型集中式缓存无法达到这样的频率。
最近,爆新科技在其Power PC处理器的较高速度版本中提出了另外两个充分的理由。爆新的架构师指出,缓存控制器越复杂,操作越慢,但这种复杂性是可取的。他们的方法是将所有复杂性放入二级缓存中,并使一级缓存简单化,但设计成能够达到最高时钟频率。在本文撰写时的设计中,一级缓存的访问时间为0.8纳秒,而二级缓存的访问时间为1.3纳秒。导致这种差异的一个因素是,一级缓存被设计为具有较高的功耗预算和快速访问,而二级缓存则被设计为以较低的功耗运行,但速度较慢。
其次,由于片上芯片必须中断执行嗅探周期,因此在嗅探到需要进行一级失效(见第4.1.1节中关于包含性的描述)时,将这些嗅探操作放在二级缓存上而不动一级缓存/ CPU复合体是有意义的。如果二级缓存位于与处理器和一级缓存相同的芯片内部,可以在二级缓存嗅探确定需要进行失效周期后最小化时间开销的情况下强制进行一级缓存失效。设计师还评估了其他方法,包括在一级缓存上使用双端口缓存标签RAM,但这消耗了太多的芯片面积并增加了标签的复杂性,从而减慢了设计速度;另一种方法是使用带有嗅探标签的较大一级缓存,但这种方法在芯片面积上成本过高,不值得采用。

日立展示了一种提高处理器Ie上SRAM内存速度的方法,该公司将其称为分离位线内存层次结构架构(SBMHA),但我们不会用这个难以控制的名称来提及它。该项目的内存设计师们意识到,将数据输入和输出SRAM的过程中,很大一部分延迟来自于需要驱动内存阵列中的长线。通常,内存阵列按地址顺序布局,较低地址位于阵列的一端,而较高地址位于另一端。其中一端比另一端更靠近读/写放大器,这一端始终具有性能优势。日立将位线(见图5.3)在长度上进行了分割,并使较快的一端成为较小的一级缓存,而较远的一端则充当二级缓存。需要使用复杂的映射来重新排列地址,以使其有意义,但这可以轻松完成。当需要在一级和二级缓存之间移动数据时,位线会重新连接,并以较慢缓存的速度完成事务。由于长位线类似于电容负载,位线越长,消耗的功耗就越大。日立方法的一个优点是,在主缓存命中时,由于有效的位线较短,阵列的功耗显著降低。在日立的芯片中,一级阵列的位线长度与二级阵列的长度相差一个数量级。
5.3 CONTROLLING THE CACHE'S CONSISTENCY WITH ITS WRITE BUFFERS
自从i486诞生以来,英特尔的Intel Architecture芯片就有了高速缓存和写缓冲区。写缓冲区存在着保存已更新数据的风险,这些数据后续可能被处理器使用。这会引发两种问题:
1. 写缓冲区中的数据未被拷贝到高速缓存(在写入周期期间发生了缓存未命中),而处理器在缓冲区更新主内存之前想要读取缓冲的内存位置。
2. 写缓冲区中的数据与高速缓存中的数据一致(在写入周期中发生了缓存命中),处理器可以直接从高速缓存中复制需要的行。如果高速缓存行没有更新,则将获取陈旧的数据。
针对这个困境,有几种可能的方法。一种简单的方法是让每个单独的写周期覆盖匹配缓存行(其设置位匹配的行)。虽然在直接映射实现中这样做是可行的,因为只有一个路需要更新,但在英特尔的四路设计中变得不可能,因为所有四路都必须变成相同的行,除非设计者希望在每个四路上都产生等待状态来检查匹配。另一个更为激进的方法是使带有匹配集地址的行无效。这意味着每个单独的写周期都会使四行缓存数据失效,这不是保证有用数据留在缓存中为处理器服务的好方法。相反,在写未命中的情况下,直到写缓冲区为空,后续读取或缓存更新才会被满足。
在第二种情况下,我们在写入周期中进行缓存命中。上面提到的问题似乎不应该是一个大问题,因为要写入高速缓存的数据应该能够直接进入缓存行中。然而,由于标记查找需要一定的时间,这会破坏时序。此外,如果要写入某个小于整个高速缓存行的数据(这些高速缓存使用多字线,而处理器支持单字节写入),则高速缓存静态随机存储器(SRAM)的复杂性必须增加,以支持对线的任意部分进行短写入周期。SRAM越复杂,它的速度就越慢。英特尔解决这个问题的简单方法是,每当有写命中时就使匹配的高速缓存行无效。这似乎是一个会减慢速度的解决方案,但它可以弥补其他地方的问题。
最后一个问题是,如果因为缓存行被覆盖而需要读取不在高速缓存中的数据,怎么处理写缓冲区中的数据?一些缓存使用一种特殊的写缓冲区,拦截读周期并满足那些包含在写缓冲区中的内容的读周期。这需要写缓冲区对缓冲区中写入的每个地址都包含一个比较器,使得写缓冲区比没有此比较器时大得多。此外,写缓冲区可能只持有请求行的一部分(例如,英特尔的写缓冲区仅保存单个字节)。由于这个原因,写缓冲区保持简单,并且后续读未命中,如上所述,强制等待写缓冲区传输到主内存,然后才允许正常读周期进行。
5.4 ACHIEVING A TRUE LRU BY USING A STACK
IBM实现了一个巧妙的缓存芯片,基于高速DRAM。该芯片包括一个高速的全相联SRAM主缓存(IBM称之为行缓冲区),以及一个更大、速度较慢的两路DRAM次级缓存,都集成在同一芯片上。尽管除了使用DRAM以外,芯片的设计大部分相对简单,但设计者们采用了一种新颖的方法来生成一个真正的LRU替换机制,用于全相联主缓存。
正如在第2章中所提到的,设计更高级联缓存的一个较为困难的方面是确保被替换的行对处理器最无用。理想的缓存应该是全相联的,并使用真正的最近最少使用算法来确定要替换的行。正如我们所知,这需要大量的位数来实现。IBM的方法确实有效,但必须付出大量的工作以满足处理器的速度要求。

图5.4只显示了缓存的主部分,即行缓冲区。有32行,每行长32字节。每个行通过一个32x25位的地址转换CAM来表示,该CAM将25位的处理器地址转换为所需的5位行地址。
LRU堆栈也是一个小型的CAM(32x5),地址转换CAM的输出被发送到LRU堆栈,确定哪个LRU堆栈位置包含重复的行号。一旦在LRU堆栈中找到此行地址,堆栈位置上方的所有行地址都会向下移动到该地址,标记它们的使用次数比当前行地址少。然后,当前行地址被加载到该堆栈的第一个条目中,使得堆栈中的行地址按照真正的使用顺序列出。这需要在单个缓存访问周期内执行位移操作,这种方法相对耗电。幸运的是,只有32个位置,每个位置只移动5位,因此功耗并不高。
现在,每次替换时最佳的行地址都将落到堆栈底部。
与第2.2.2节中关于关联性讨论相比,我们展示了每个缓存行需要N!位来存储真正的LRU,而这种方法只使用32x5位,即160位。由于这个缓存中只有相当于一行,而且这一行有32路关联性,传统的方法需要表示32!个状态,可以用log2(32!)位编码,或者118位。所使用的位数之间的差距仅为26%,足够小,我们应热烈欢迎使用更直观的基于堆栈的方法,而不是使用更深奥的最小编码技术。
5.5 A "SEMIASSOCIATIVE" CACHE

这是一个IBM构思的相对复杂的混合缓存:一个完全关联的缓存和一个八路组相联的缓存。它展示了当你将几乎无限的智力投入到有限的晶体管数量中时会发生什么。缓存的基础是一个八路组相联的物理缓存,使用单字线。这没有什么特别之处。问题在于增加了一个基于CAM设计的额外目录。这在图5.5相对复杂的图表中有所展示。通过添加CAM,缓存的访问时间不会受到大多数多路缓存中路径选择机制延迟的影响。
CAM在虚拟地址位的八位子集被翻译为实际地址之前查看。缓存的CAM内容类似于MMU的页表CAM内容。通过在地址被MMU翻译之前在额外的CAM目录中查找虚拟地址,缓存数据RAM的路径选择可以提前进行,从而在大多数情况下可以得到预期的正确数据路径(毕竟,我们在处理统计数据)。在这种应用中,使用CAM而不是缓存标记存储器的一个优点是,当虚拟地址呈现给阵列时,搜索即刻开始,而不需要等待地址解码的发生。每一行的每个标记与其他标记同时查看虚拟标记位。当然,数行的每个路可能都包含匹配的虚拟地址位,因此六个组地址被解码,只有与匹配的组地址相关联的CAM虚拟地址匹配才被允许路由到数据RAM的路选择多路复用器。
在进行CAM比较时,缓存标记RAM和缓存数据RAM也会解码六个组地址位。当缓存数据RAM的八个路的所有数据都准备好时,CAM搜索已经使多路复用器能够将相应路的数据传输到处理器的数据输入引脚上。如果由于某种原因在任何路中都没有匹配,CAM的下游逻辑会告诉处理器发生了缓存未命中。
同时,地址正在MMU中从虚拟地址翻译为物理地址,并且MMU的36位输出与缓存标记RAM中相应路中的36位标记位进行比较。这是验证CAM是否选择了缓存数据RAM中正确的路,甚至是否正确地识别了命中的酸性测试。以防万一在错误的路中出现了虚假命中,目录会在随后的周期内对所有八个目录路上的MMU输出进行比较。通过这样做,缓存可以迅速恢复由CAM产生的虚假命中。
总的来说,可以避免图2.11中的长延迟路径,并实现类似于图2.10中的更快路径,即使缓存实际上是一个八路组相联的设计。在这个设计中,IBM使用了二转子CAM单元构建了缓存,因此CAM的芯片面积成本大约是基于六转子单元的缓存数据RAM阵列的两倍。可以相信,没有等待状态的一定大小的八路组相联缓存性能优于具有一个等待状态的三倍大小的缓存。另一方面,有趣的是猜测,如果八路组相联设计(在没有CAM的情况下将会导致使用等待状态)让位给直接映射的零等待设计,IBM是否可以通过使用更大的零等待直接映射设计来简化。这可能取决于系统运行的软件。
5.6 CONTROLLING ALIASES BY USING EIGHT COMPARATORS
在其中一款处理器中,AMD采用了一个逻辑指令缓存,并以一种真正强力的方式解决了别名问题。缓存中设置位的数量比页面内索引地址位的数量多两位。这意味着别名的可能性存在真正的问题,正如2.2.1节所描述的那样。
缓存标记存储了被缓存位置的物理地址。当一个页面索引地址输入到缓存中时,它可能被映射到由重叠位的四种可能状态指示的任何四个位置之一。因此,要么处理器必须等待MMU翻译地址后才能确定周期是缓存命中还是缓存未命中,要么必须在MMU翻译之前评估与索引地址相关的所有四个集合地址的标记。
AMD选择了第二个方案,同时检查可能表示给定集合地址的每个可能缓存行。在每个处理器周期中,通过对缓存的两个路的每个可能命中位置(由两个重叠位确定)进行比较,同时进行八次比较。踪踪周期的处理方式也相同,需要同时完成八个踪踪周期。这种方法需要大量的逻辑,但确实解决了别名问题,而无需将缓存速度降低到MMU所需的速度。
5.7 WRITE BUFFERING VS. MULTIPROCESSING
英特尔体系结构规范涵盖自8086以来的所有x86架构版本,并致力于理解自该架构诞生以来所进行的多次升级。值得称道的是,奔腾Pro处理器及其后续设备的设计师们必须考虑到多处理、缓存、多程序运行和写入缓冲的综合效应。以下说明将展示英特尔采取的一些方法,以在所有这些需求的冲突要求中保持某种程度的合理性。
迄今为止,本文还未涉及的一个问题是多处理系统中处理器在上电复位后如何确定彼此身份。在某些系统中,每个处理器由坚固的硬件定义,因此,也许总线上的第一个处理器是调度器,离该处理器越远的位置根据其物理位置被分配一个编号。在其他系统中,处理器共享专用的通信通道,因此处理器1可以与处理器2通信,2与3通信,依此类推。最令人费解的方法(但在公开发表的论文中经过深思熟虑)是,所有处理器都有平等机会成为调度器或从属处理器。尽管英特尔还支持一种不太对称的方法,其中处理器可以在上电时硬连接为“引导处理器”,这样可以简化软件开发。
对于这种类型的多处理,支持相当简单。必须将总线“锁定”,以防止其他处理器在一个处理器修改该位置时访问共享位置。这样,首个访问该位置的处理器可以说“我是#1”,第二个处理器可以观察到并说“那么我是#2”,依此类推。为了实现这一点,需要一种机制,使处理器能够检查一个位置并确定其内容,然后修改这些内容,而不会被另一个处理器同时读取或更改。这通过“锁定”周期来处理。锁定周期涉及读取后跟写入,或在某些情况下是写入后跟读取。最典型的锁定周期是增量或减量指令(读取、增量和写入)。
如果通信位置仅在回写缓存中复制而不在主内存中,则这些指令将无法正常工作。当存在这些锁定周期时,英特尔提供了一种使处理器不向其他处理器报告这一情况的方法。读者可能觉得这很奇怪,直到反思了可以利用读/修改/写周期进行处理器之间的通信的某些情况,但被通信地址只有间断性地被其他处理器读取。由于有理由通过将这些位置转为修改状态以加速性能(这是英特尔体系结构中所倾向的方法),因此英特尔在回写缓存中添加了支持锁定周期所需的硬件。他们称之为缓存锁定。
当存在写入缓冲区时,情况变得更加复杂。在大多数带缓冲区的缓存中,写入缓冲区通常不必过多考虑数据的位置。如果缓存位置被更新,通常会在缓存中进行更新(对于回写缓存)或者在数据写入写入缓冲区时使其无效(对于写透缓存)。在分配的缓存中,首先读取数据,然后更新写入缓冲区的数据和缓存行。如果该位置恰好是处理器进行通信的位置,并且是一个页面既指定为写透又指定为回写的处理器,则会变得棘手。在英特尔体系结构中,写缓冲区中的行代表在写命中时已失效的缓存行,而且该行所在的页面被指定为写透。由于架构允许虚拟内存页面重叠,并且允许将单个页面声明为写透、回写或不可缓存,缓存控制器为重叠范围选择最简单的缓存选择。
奔腾II处理器具有16个加载缓冲区和12个存储缓冲区,每个缓冲区都可能包含与缓存内容冲突的数据,除非得到适当管理。读周期会检查写(存储)缓冲区,因此可以简化某些周期。某些读缓冲区通常用于读取猜测的分支周期,并且需要在任何时候存在疑问时清除,以防分支目标被写周期修改。所有写缓冲区都在某些情况下写入主内存,包括执行I/O周期的任何时候(以防I/O周期启动需要当前主存储器的DMA)。有一个“组合写入”周期,将写入周期组合成单个缓存行写入。这通常用于未缓存的空间中写入屏幕内存。为了避免进一步复杂化处理器排序,不使用字节聚集。写分配用于在写失误周期中替换行。当对未缓存的地址执行锁定周期时,清除缓冲区,以便在读/修改/写周期中读取和写入周期发生时没有任何歧义。换句话说,在这种情况下,处理器从弱排序移动到强排序。如果将地址缓存为写透,或者如果缓存行为回写但处于共享状态,则会标记缓存副本无效。如果该行位于回写位置并被标记为排他性,则将其带到已修改状态,并且不缓冲写入。如果这似乎困难,请考虑作者选择不尝试绘制该过程的复杂性事实。它无法适合第4章中找到的类型的图表中的任何一个。虽然确定这些情况的细节可以让人头昏眼花,但最终要考虑每种情况,确定每种情况的适当响应,然后实施状态机(它总比挑战所示的小)以使这些状态相互匹配。英特尔决定允许的另一个奇怪情况是自修改代码情况。尽管指令缓存不需要写协议,但数据缓存有可能包含对指令缓存中代码的更新。仅出于这个原因,英特尔架构具有钩子,帮助指令缓存窥视数据缓存,并反映程序可能想要对代码造成的任何更改。


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值