2.5 指令Cache

来源:互联网 发布:柏曼灯具怎么样知乎 编辑:程序博客网 时间:2024/05/22 06:22

在一个处理器系统中,指令Cache与数据Cache的组成方式和使用规则有所不同。在现代处理器系统中,在L1 Cache层面,指令Cache与数据Cache通常分离,而在其后的Cache层次中,指令与数据混合存放,在多数情况下L1指令Cache是只读的,因此Cache Block中包含的状态较少一些,一致性处理相对较为简单。

与指令Cache相比,数据Cache的设计与实现复杂得多。在此回顾指令Cache的主要原因是,在之后的篇章中,我会专注于介绍数据Cache。在目前已知的微架构中,x86体系结构的指令Cache和指令流水线的设计最为复杂。所以本节只介绍x86处理器中指令Cache和与其相关的设计。这些复杂度与x86处理器不断挑战着处理器运行极限直接相关,此外由x86Backword-Compatibility而继承的变长CISC指令也需要对此负责。这些变长指令对于设计者是一个不小的灾难。在这场灾难中,x86架构久病成良医促成了一个又一个的发现。

通常情况下,工业界很少有大的成果能够领先于学术界。更多的情况是理论上较为成熟的技术在若干年之后被Industry采纳。Intel的伟大之处在于在微架构的很多领域中领先于理论界。更加令人深思的是,Intel的很多发现其起源是为了解决自身的并不完美。

Intel的每一代Tock,几乎都是从Branch Predictor的设计与优化开始,并基于此重新构建指令流水与Cache Hierarchy结构。在微架构中功耗与性能的权衡主要发生的领域也集中于此。在处理器系统中最大的功耗损失莫过于把一些已经执行完毕的指令抛弃和一些不必要重复的操作。在指令执行阶段,Misprediction将刷新指令流水线,将丢弃很多In-Flight的指令,对于x86处理器,丢弃一条最长可达256b的指令,是一个不小的损失。而Cache Hierarchy是微架构耗费资源最多的组成部件。一个处理器将广义和狭义Cache去掉之后,所剩无几。

Intelx86处理器的Tick-Tock的不断运行过程中,Branch Predictor的设计依然在不断发展,指令流水线的设计也随之改变。虽然指令流水线并不是本篇所关注的重点,但仍有必要在此介绍一些与指令Cache直接相关的内容。在x86微架构中,一条指令流水线通常被分为Front-EndBack-End两大部分。在Front-EndBack-End之间使用DQ(Decoder Queue)连接,其结构如218所示。

2.5 <wbr>指令Cache

Front-End负责指令的FetchesDecodes,将指令发送给Decoder Queue,相当于Producer;而Back-End在条件允许的情况在从Decoder Queue中获得指令并执行,相当于Consumer。指令在Back-End中执行完毕后,需要将结果反馈给Decoder Queue,进一步处理Depedency和资源冲突,同时还需要为Front-End中的Branch Predictor反馈Branch指令的最终执行结果。Branch Predictor使用这些反馈确认是否发生了Misprediction

Front-EndDecoder QueueBack-End之间需要协调工作,以保证整个流水线的顺利运转。理想的情况是Front-End可以将指令源源不断地发送给Decoder Queue,而Back-End可以顺利地从Decoder Queue获得指令,采用这种方式似乎只要不断提高Front End送至Decoder Queue的指令条数(Instruction Block),就可以不断的提高ILP

但是这个Block数目并不能无限制提高,因为在DQ中的指令流已经被Branch指令切割成为若干个子指令流,物理地址连续的指令流从程序执行的角度上看并不连续,为此现代处理器多设置了强大的Branch Predictor猜测程序即将使用的子块。

但是对于一个指令流水线过长的微架构,一次Misprediction所带来的Penalty仍然足以击溃指令流水的正常运行。基于这些考虑,IntelPentium IV处理器中,率先使用了Trace Cache机制以最大限度的使指令流水线获得事实上连续的指令流。

从本质上看,TraceCache机制是一个机器的自学习加修正策略,试图让机器尽可能的分析本身难以琢磨的指令流,捕获其运行规律,使得指令流水获得的指令尽量连续。从整个微架构的发展历史上看,少有这样级别机器智能的成功案例。为什么偏要赋予仅能识别01这两个数字的机器如此重任。从历史的进程上看,从微架构中流传下来更多的是简单精炼的设计。偏执的Pentium IV使用了Trace Cache

这个后来被JohnDavid称为“likely a one-time innovation[7]的技术,在当时依然受到热捧,“Trace cache: a low latency approach to high bandwidthinstruction fetching”这篇文章被公开索引了五百多次。

当时依然有很多人质疑使用TraceCache提高的性能与付出的代价严重不成比例,但是有更多的学者在大书特书这个主题,根据这些文章提供的模型和相关的Quantitative Analysis,不难得出Trace Cache是一个伟大发明的结论,直到高频低能的Pentium IVAMDOpteron彻底击败。尽信量化分析结果不如没有。

Trace Cache的组成结构和使用方法不在本书的讨论范围内,对此部分有兴趣的读者可以参考[68]获得详细信息。在Intel后继发布的微架构中已不见Trace Cache的踪迹,但是依然不能全盘否定Pentium IV构架使用的指令预取,其详细过程如219所示。

2.5 <wbr>指令Cache

x86架构中,FLC被分解为指令与数据CacheMLCsLLC同时存放这指令与数据。这种方式是当代绝大多数微架构采用的Cache组成方式,也被称为Harvard Architecuture,更为准确的称呼应该是Modified Harvard Architecuture

PentiumIV架构中,指令将从L2 Cache首先到达Instruction Stream Buffers,为简化起见,本节不讨论所预取的指令在L2 Cache Miss的情况。只要Instruction Stream Buffers不满,预读地址有效,控制逻辑就会将指令从L2 Cache Lookup单元源源不断地传递下去。

如果在Instruction StreamBuffer中的指令为Conditional Branch时,该指令将首先被送至Front-End BTB进行查找,如上文所述,在一个程序执行过程中使用的指令流并不连续而是被Conditional Branch分割成为若干个子块,因此需要对这类指令进行特别的处理。

这些指令将被保存在Front-EndBTB中,这个Front-End BTB也是一种广义Cache,其组成结构和使用方式与狭义Cache类似。因为在Front-End BTB中只有4096Entry,所以Miss时有发生,在Miss时,需要根据相应的替换算法淘汰一个Entry,并重新创建一个Entry,但是并不会改变指令的预读路径,同时Conditional Branch还需要转交给Static Branch Predictor做进一步处理,本节不关心这种情况。

如果Hit,需要检查BTB预测的结果是Not Taken还是Taken。如果是Not TakenBTB不会通知指令预读单元改变预读路径;如果是Taken,则将BTB预测的Target Address送至指令预取单元,最终传递到L2 Cache Lookup单元,将Target Address指向的指令数据向下传递给Instruction Stream Buffers

ConditionalBranch从指令单元中Retire时,会将Commit的结果传递给Front-End BTBTrace Buffer BTB。如果Conditional Branch最终的执行结果与BTB的预测结果相同时皆大欢喜。不同时即为Misprediction,会带来一系列严厉的系统惩罚,不再详细描述这个过程。

Instruction StreamBuffers会将CISC指令继续传递给IA μop DecoderDecoder将绝大部分的CISC指令翻译成为μops并送入Trace Cache,同时按序送入Decoder Queue。此时在Trace Cache中保存的是μops,而不再是原始的CISC指令。部分CISC指令,如会被送入到Microcode ROM,进行查表译码,这些CISC指令较为复杂,通常会被分解为5条以上的μops,设立Microcode ROMx86架构的无奈,我们忽略这个无奈。     

译码后得到的μops将依次进入Trace Cache。其中Conditional Branch译码后得到的μops需要进行额外处理,因为Trace Cache的实现要点是记录这些Conditional Branch连接而成的指令流而不是物理地址连续的指令流。

Pentium IVTrace Cache设立了专用的BTBTrace Cache BTB,并且希望其命中率最好是100%,这样Decoder Queue中的μops可以全部来自Trace Cache中已有的已经完成译码的指令集合。这只是一个理想情况,依然存在着Branch Target没有在这个BTB中命中的情况,此时依然需要回退到Address Translation部件,进行指令预取。Trace Cache BTB依然会监听Conditional Branch最后的执行情况,并做进一步处理。

Pentium IV微架构在Front-End所做的各种努力,并没有改变其高频低能的结局。这一个几乎给Intel带来浩劫的微架构最终被抛弃,以色列IDC团队的Core Architecuture拯救了IntelIntel启动了Tick-Tock计划,更加优秀的微架构Nehalem横空出世,很快是Sandy Bridge

Nehalem没有保留Trace CacheSandy Bridge微架构也没有。但是Pentium IVFront-End并没有轻易地被抛弃,我们依然可以在NehalemSandy Bridge微架构中找到源自Pentium IV的设计,其Front-End的组成结构如220所示。

2.5 <wbr>指令Cache

NehalemFront-End源自Core Archtecture,与Pentium IV有较大差异。Pentium IV巨大的失败阴影左右了Intel后续微架构的设计。Pentium IV流水线的多数设计没有被继承,除了一个源自Trace Cache的附带品。x86架构的CISC指令过于冗长,与其他微架构相比译码过程也复杂很多。在Trace Cache中保留经过已经完成译码的μops是一个不错的设想。

Merom微架构继承了这个设想,使用Instruction Loop Buffer保留这些μops,在Nehalem中这个部件被称为μops Loop Buffer,最后Sandy Bridge使用一个1.5KBμops Cache保留这些已经完成译码的μops[1]

这个Decoded μopsCache也被Sandy Bridge微架构称之为L0 Cache[1]Sandy Bridge微架构采用L0指令Cache除了可以保留珍贵的译码结果之外,更多的是基于Power/Energy的考虑。如果在一个程序的执行过程中,检测到Loop后,将直接从μops Cache中获得指令,暂时关闭并不需要的部件以节约功耗。

历经NehalemSandy Bridge两轮Tock之后,x86处理器在Branch Predictor部件的设计中虽然仍有调整,但是日趋稳定。x86微架构引入Two-Level Adaptive BranchPrediction[47]机制之后,BTB的变化更加细微。除了μops Cache之外,Nehalem微架构Branch Predictor使用的Gshare PredictorIndirect Branch Target ArrayRenamed Return Stack Buffer这几个重要部件被Sandy Bridge架构继承[70]

PentiumIV相比,CoreNehalemSandy Bridge微架构简化了Front-End的设计,也使我失去了进一步使用文字叙述的动力。但是与其他微架构,如PowerMIPS相比,不论是Nehalem还是Sandy Bridge,在Front-End的设计中依然投入了大量的资源。对于x86体系结构,也许在放弃了Backword-compatibility之后,才能真正地改变Front-End的设计。也许到了那一天,Front-End这个专用术语也会随之消失。

0 0
原创粉丝点击