在一个处理器系统中,指令Cache与数据Cache的组成方式和使用规则有所不同。在现代处理器系统中,在L1 Cache层面,指令Cache与数据Cache通常分离,而在其后的Cache层次中,指令与数据混合存放,在多数情况下L1指令Cache是只读的,因此Cache Block中包含的状态较少一些,一致性处理相对较为简单。
与指令Cache相比,数据Cache的设计与实现复杂得多。在此回顾指令Cache的主要原因是,在之后的篇章中,我会专注于介绍数据Cache。在目前已知的微架构中,x86体系结构的指令Cache和指令流水线的设计最为复杂。所以本节只介绍x86处理器中指令Cache和与其相关的设计。这些复杂度与x86处理器不断挑战着处理器运行极限直接相关,此外由x86的Backword-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-End和Back-End两大部分。在Front-End与Back-End之间使用DQ(Decoder Queue)连接,其结构如图2‑18所示。
Front-End负责指令的Fetches和Decodes,将指令发送给