你手机里的Arm芯片是RISC,电脑里的x86是CISC,这个分类你大概听过一百遍。但要说Arm省电是因为RISC架构,这话经不起推敲。指令集本身解释不了最大的差异。

打开网易新闻 查看精彩图片

更反常识的是:原文指出,30多年来高性能x86核一直在使用与RISC相关的技术,而大核Arm现在也在使用与CISC相关的技术。RISC和CISC标签仍能说明软件看到的指令集,但已远不能描述实际运行它们的硬件。

一、CISC芯片当年是围着微码转的

1978年的Intel 8086,大量x86指令靠微码控制执行。这些例程存在片上ROM里,指挥完成一条指令所需的更简单步骤。Ken Shirriff的反向工程发现,它的控制存储包含512条微指令,每条21位宽,支撑约75种指令类型。

微码是当年实现复杂指令集的常见手段,大型机把它玩得更狠。斯坦福的John Hennessy和伯克利的David Patterson在图灵奖演讲里提到,VAX有20%的指令占了60%的微码,却只贡献0.2%的执行时间。RISC就是他们对这个问题的回应。

RISC的思路很直白:给软件更简单的指令,让硬件不用微码序列就能执行,把芯片资源更多花在寄存器和指令缓存上。1980年代初,斯坦福的MIPS和伯克利的RISC-I验证了这条路。两人后来描述过DEC的一次历史对比:CISC每条程序需要的指令更少,但每条指令要更多周期,同等工艺下RISC大约快四倍。

Patterson在DEC帮忙修VAX微码时亲眼见过这种复杂度。他提出过芯片出货后修复微码的办法,论文被IEEE Computer拒了,审稿人反对把处理器设计得复杂到需要这种修补。按Patterson的说法,结论很直接:这么设计电脑是蠢办法,加内存、加成本、还要打补丁,简直疯了。

讽刺的是,Intel和AMD现在 routinely 给用户手里的芯片推微码更新。

二、x86一颗芯片一颗芯片地把RISC技术搬了进来

到1980年代末,不少研究者认为x86要输了。后来当上Intel Pentium Pro高级架构师的Bob Colwell回忆,Intel内部有人劝管理层放弃CISC,公司选了另一条路:不改指令集,改执行方式。

1989年的486展示了不改x86能变多少:五级流水线,条件有利时每个时钟维持一条简单指令。复杂指令还是要更久,一条更新内存的add光在执行阶段就要三个周期。

更大的变化在1990年代中期,但Intel不是第一个。NexGen的Nx586在1994年把x86指令翻译成更简单的操作交给内部核心,这是x86世界第一次RISC式改造。AMD的K5随后跟进,还占了便宜——首席架构师Mike Johnson此前设计过AMD Am29000,据报道K5核心大量借鉴了超标量29000的设计,解码器把x86指令转成一到四个内部操作。

Intel在1995年11月用Pentium Pro跟上,解码器把x86指令翻译成更简单的内部操作,叫micro-ops或uops。当时被描述为遵循RISC式的load/store模型:一条对内存值做加法的x86指令,可以拆成load、add、store三步,而传统RISC程序会把这三步写成独立指令。

用微操作的好处是方便套用高性能RISC设计的手法。核心可以乱序执行就绪的操作,再按程序顺序提交结果,软件看到的行为不变。微码也没消失,硬件解码器处理常见指令,复杂情况走更长的微码序列。Colwell举过一个例子:通过门调用的远调用可能需要200个微操作。区别在于,普通代码不再每条指令都依赖那条慢路径。

三、大核Arm现在干起了x86的活

现代Arm核同样把指令拆成微操作,有时甚至几十个。日本富岳超算用的富士通A64FX上,SVE指令FADDA会展开成63个微操作,执行有序浮点归约并把向量元素累加进标量累加器;LDADD展开成四个,把内存读、加法、写合并进一条指令。RISC指令不一定对应简单的硬件操作。

Arm还从x86地盘借了另一样东西:缓存。Arm核会缓存已解码的操作,避免重复解码同一条指令。2019年的Cortex-A77加了1.5K条目的宏操作缓存,术语和x86的微操作缓存不同,目的类似——复用解码成果,提升吞吐,减少取指和解码活动。

这不代表AArch64变成了CISC架构,它仍然是定宽32位指令,普通算术一般和内存访问分开。但一条指令照样可以描述一大摊工作,向量和原子操作尤其如此。核心越宽越激进,内部机制就越长得像,哪怕指令集依然不同。

四、不是所有人都认“x86里有RISC核”

包括设计它的人。把x86的微操作叫“类RISC”,是有前提的,Pentium Pro的架构师Colwell本人就不同意。他反对混淆软件看到的指令集和实现它的硬件。他的辩护很简单:指令集是对软件的承诺,微架构是某颗芯片兑现承诺的方式。设计者可以改流水线、改解码器、改执行机制,只要程序行为不变,Pentium Pro就是这么干的,它底下再不一样,仍然是x86处理器。

内部微操作不是软件能瞄准的指令,Pentium Pro的118位格式携带的是给处理器自己用的信息。拿它的宽度和32位RISC指令比,说明不了两种设计的复杂度。后来的Intel核还用微操作融合,让一条x86指令里的load和算术操作在核心部分环节共用一个条目。

混乱来自“RISC”这个词被同时用于指令集哲学和早期RISC处理器的实现技术。x86保住了指令集,同时吸收了大量实现手法。说实现的一部分“类RISC”没问题,但你的软件看到的只有x86指令。

五、指令集确实让x86付出代价,但没你想的那么大

x86的变长指令让解码更复杂,可要准确说出代价有多高反而更难。2016年一项针对Intel Haswell Core i7-4770的研究,用Intel自己的能耗报告加统计模型估算解码功耗,在两个选定的微基准里,解码约占封装功耗的3%和10%,研究者称换成别的指令集“只会省下少量功耗”,因为指令解码无法避免。注意这只是单颗Haswell核心,不是x86对Arm的全面评估。

现代x86核用微操作缓存减少重复解码。AMD Zen 5有6K条目的缓存,Chips and Cheese测试发现关掉它,SPEC CPU2017整数性能下降20.3%。复用解码指令收益不小,但不普适:同一轮测试里,《赛博朋克2077》和《GTA V》几乎没差别。收益完全看负载。

Arm的Cortex-A715走了另一条路。2022年推出的它砍掉AArch32支持,简化解码器,还去掉了前代的解码操作缓存,说明更简单的解码会改变核心的取舍。x86的遗留运行模式是另一笔兼容性负担,去掉它们也不会让剩下的指令变成定宽。

还有证据反对把任一类指令集当成天生更高效。2013年另一项研究在其考察的处理器和负载中,没发现两类指令集有根本的能效优势。这不说明今天x86的开销如何,但支撑一个更大的点:芯片的设计目标、制造工艺、缓存、分支预测器、电源管理都算数。一台设备省不省电,远不是CISC和RISC两个词能概括的。