以前低功耗和高算力处理器算是泾渭分明的。比如Arm前身Acorn起家的立身之本就是低功耗——在ARM指令集发展的早些年头里,应该不会去想跟x86或者POWER这种向来做高算力的指令集及其生态一较高下。这在一般人看来,根本就是两个行当。
低功耗领域,就算2013年苹果在iPhone发布会上说A7芯片(Cyclone核心)是桌面级架构,消费市场几乎也没人把这话当回事。从直觉来看,手机处理器、基于Arm指令集,还“桌面级架构”?做梦呢吧。直到苹果将M1变为现实,与此同时Arm本身在数据中心市场越来越有所斩获,才让更多的人惊觉:那个靠低功耗起家的Arm也能做高算力。
另一方面在高算力方向,早年HPC(High Performance Processing)和真正的高算力设备是不大在意能耗问题的,无非是多用几度电的事情。就好像台式机插电工作,其功耗再如何都不能跟空调比;唯有笔记本这种偶尔需要靠电池运转的设备,才将功耗纳入考量。
但从2009年开始,越来越多的文献给出高算力设备耗电惊人的证据。那时候有人将HPC耗电去跟核电厂的发电量做比,超算要省电、数据中心要节约电费被提上日程。苹果说Mac Studio相比高端台式机每年减少1000kWh的能耗;英伟达说要是全世界所有AI、HPC和数据分析工作都跑英伟达GPU服务器,那每年的节电量相当于200万辆汽车一年的消耗。
原本的低功耗指令集走向高算力,而高算力设备也开始追求能效。但历史遗留思想还在:就是某种指令集是针对低功耗的——不适合高算力,以及某种指令集天然适合做高算力——功耗低不下去。
Arm证明自己也能做高算力之后,仍有不少人认定x86做不了低功耗。有这样的想法并不奇怪,毕竟Intel此前几度尝试攻占手机市场都未能成功;且x86 CPU在笔记本PC平台的续航,大方向暂时无法和苹果、高通这样的Arm阵营选手一较高下;加上早年还有IBM用于服务器的POWER下放给笔记本处理器彻底失败的案例在先,更让人感觉功耗还是跟指令集强相关的。
真的是这样吗?
x86做不了低功耗的争论
我们先来谈谈指令集(ISA,Instruction Set Architecture)是个啥。指令集自然就是指一组指令了。那指令是什么?加减乘之类的操作就是指令,1+1就是一个具体的指令。指令集之所以重要,究其根本是因为指令集是一个“抽象层”。
处理器是一种硬件芯片,上面有一大堆的晶体管,晶体管构成逻辑门,逻辑门构成功能模块,功能模块构成执行单元,各种控制和执行单元合起来,构成完整的处理器微架构,最终实现操作和复杂计算。这里面有一个问题,就是处理器作为一种硬件,如何与软件沟通,或者说软件如何让处理器知道、理解自己要做什么。
来源:Intel
指令集作为软件和硬件之间的“抽象层”,就跃然纸上了。指令集是一组指令,定义在硬件中可以执行哪些类型的操作。它就像是个字典,指导软硬件的沟通。对硬件而言,设计CPU需要以指令集为规范;对软件而言,编译器通过ISA将各种高级语言(比如C、Java)编写的代码转化为机器代码指令。
所以Arm、x86、RISC-V之类的指令集,本质上定义了自家处理器及其生态的规范——要跟我混,就得依照咱的规矩来。x86指令集阵营选手主要有Intel、AMD;Arm指令集阵营选手主要有苹果、高通、Ampere Computing等。这么复杂的东西,我们当然不大可能以“白盒”的方式一点点剖析指令集对功耗有多大影响。但我们可以说说有关x86做不了低功耗的一些主流争论点。
首先是老掉牙的CISC(复杂指令集)和RISC(精简指令集)之争。x86被认为是CISC方面的某种经典代表,而Arm隶属于RISC。有关CISC适合做高算力,RISC则擅长低功耗的说法是由来已久的。
第二个比较常见的说法是,Arm采用定长指令,而x86为变长指令(即指令长度是不定的)。变长指令就意味着,CPU首先得搞清楚这指令有多长,就让指令的解码工作变得更复杂。解码作为处理器工作的重要环节,就让x86指令集处理器天然有了功耗方面的弱势。
还有一个说法,x86作为一种生态庞大、历史悠久的指令集,必然是拖着兼容性这个毒瘤致老态龙钟,而显得冗杂、低效,所以做不了低功耗。
有关RISC的古老传说
在RISC精简指令集这个概念于上世纪80年代由David Patterson首度提出时,最初理念是让指令集做到简化,实现绝大部分指令都能在一个时钟周期内执行完成,这样也就确保了较短的周期时间,以及更简单的设计——CPU设计起来更容易、成本更低、更快。其核心理念在于,当大家都有100万个晶体管的时候,那么应该用执行更多小型指令的方法,实现更高的管线效率,而不是浪费在一大堆大型的复杂指令上面。
不过有关CISC复杂指令集与RISC的论战实在是太多了,多到我们再提起就很火星的感觉。但指令集与功耗高低的强相关,CISC与RISC之争的确是个中关键。Chips and Cheese去年写过一篇文章谈Arm和x86之间是否真的存在谁更适合做低功耗的问题,也首先谈到了CISC和RISC之争。
实际在历史发展长河中,所谓的CISC和RISC是在不断趋同的。一个经常被人提起的例子是,Intel从Pentium Pro时代开始引入μop(微操作),就是在处理器拿到某个复杂指令时,会将其先拆解为多个μop,再后续做执行。举个经典的例子,CISC支持一个数字直接去加上某个内存地址(中的数字),但对RISC而言——这个操作要分3步,就是先从内存地址中load数字,然后将其与现有数字相加,最后将数字再存回到内存。其实对近代CISC处理器而言,也会将原本1个指令拆分成这样的3个微指令。
或许x86走向部分RISC特性的历史时间会更早。相对的RISC指令集也在几十年的发展中,不断汲取x86世界的既有技术,这两者间的界限早就越来越模糊。如果说CISC和RISC从早年就划分出高算力和低功耗之别,那么IBM POWER作为RISC指令集一员,曾在HPC领域风光一时,就已经彻底粉碎了这一传说。
但基于CISC的传说,延伸出的一个问题是x86引入了μop,令其更向RISC精简指令集靠拢。这是否意味着Intel需要在指令执行前多一个步骤、多一些晶体管、多一些能耗来执行μop的拆分操作呢?这是此前不少人谈论CISC不行的一大论据。
Arm的解码开销也不小
从Chips and Cheese的解释来看,现代Arm指令集CPU也需要执行切分μop的操作。而且对不少Arm处理器(如Marvell的ThunderX芯片)来说,对μop阶段的各种调整是作为架构更新的重要组成部分存在的。
还有比较典型的像是这两年很红的超级计算机富岳(Fugaku),其中来自富士通的A64FX超算芯片同样基于Arm指令集。从其架构手册来看,A64FX也会将大指令解码为多个μop。所以μop并不是CISC或x86的“专利”。
更何况还有Arm SVE(Scalable Vector Extension)/SVE2这种扩展指令更是会拆分成一大堆的μop。比如FADDA指令会切成63个μop,其中有部分μop本身的延迟就达到了9个周期;A64FX也支持直接从内存加载一个值,并执行相加操作,以及将结果存回内存中(LDADD指令),这样一个指令会被切分成4个μop。在这样的背景下谈论CISC、RISC不再有任何意义。
另一个比较重要的依据是,如果说Arm架构的解码开销真的那么低,Arm也不会在微架构中引入μop cache了。这种cache的引入就是为了避免重复的指令读取和解码——x86那边很早也已经开始这么干了。
Cortex-A77微架构中加入了1.5k条目的op cache。据说Arm对此是下了不少功夫的,光是debug其中设计就花了至少半年时间。后续Cortex-A78、A710,以及更大的Cortex-X1/X2微架构均有此设计。
三星在自研的Exynos M5微架构(应用于Exynos 990芯片,Galaxy S11手机)中也引入了μop cache,作为向后续管线提供μop的途径之一,以此来节约取指和解码的功耗。可见以CISC和RISC之分,来说取指-解码阶段RISC的优势,并证明x86做不了低功耗,在当代是一点也不靠谱的。
变长指令对x86的实际影响
还有个问题就是前文已经提到过的,CISC的变长指令特性终究令其解码不够高效,因为指令长度不定就意味着首先需要明确一条指令有多长的问题,也是要浪费晶体管和功耗的东西。
其实业界对此做评估的研究和实验还是不少的。Chips and Cheese曾经尝试将op cache禁用(以便取指解码操作必须进行),发现在这种情况下,AMD Zen 2核心层面需要多消耗4-10%的功耗,如果是封装级别的话则多消耗0.5-6%的功耗。理论上,如果只将其中的解码阶段拿出来,则功耗占比会更低。
而且Chips and Cheese还强调他们所做的测试只相关于L1 cache,而不曾涉及L2, L3 cache以及主内存——如果更多层级的存储系统考虑进去,那么解码阶段在此占据的功耗将低到可以忽略不计。以及对于某些关掉op cache,功耗反而降低的测试项目,解码占据的功耗数值已经湮没在处理器核心的其他组成部分里。
可见在开启op cache以后,变长指令这一特性对于解码的影响实则根本就没有多大。更多系统性研究也显示,对于Intel的x86处理器而言,解码从来不是造高能效处理器的障碍;早期的Haswell和Ivy Bridge架构均有人验证过。不过或许,“低功耗”低到某种程度,大概还是需要在功耗构成里去抠一些部分出来,毕竟占比小也不是没有占比。
生态的影响
谈论指令集这个问题,实则还涉及到“扩展指令集”。扩展指令集可以理解成向字典中添加新词——新的指令是为了更高效地执行某些特定的操作,利用微架构中新出现的处理单元来加强某些特定工作的效率。Intel x86处理器中的SSE/AVX,Arm处理器中的Neon/SVE都属于扩展指令集。
不过这往往牵扯到生态问题,就是在添加了新的指令以后,开发者有没有即时地用上这些指令。如果没有用上,则意味着新设计浪费了,效率上不去。一般PC评测媒体喜欢用Cinebench这个跑分工具,来测试某颗处理器的性能;我们为什么总是选择更新版本的Cinebench?而不坚持一直用老旧的Cinebench R15呢?
Intel过往新增的扩展指令,来源:Intel
这是因为Cinebench R15程序本身连x86的AVX/FMA/AVX2都不支持,这对做出更新的处理器而言,岂不是无法反映性能提升?即便是最新版的Cinebench R23都尚不支持Intel x86的AVX512指令。真正做出AVX512,甚至更早AVX2指令优化的应用都不算多,尤其在PC个人计算机应用市场。
比较有趣的一个例子是,此前苹果M1 Ultra在Cinebench R23测试中的跑分成绩比不上Intel酷睿i9。大量苹果用户表示Cinebench R23程序跑Arm Neon,是基于对x86 SSE的“转译”,使得M1芯片跑分成绩大打折扣。不评论这个说法对不对,起码这足以说明某一个指令集阵营的生态发展水平,也就是软件侧的发展情况,对其高算力、低功耗与否也有很大的影响。
更专用的指令似乎是这两年数据中心CPU和其他处理器发展的主旋律,比如Intel Sapphare Rapids的AMX指令集,主要针对AI计算。这类新特性的加入实则让不同处理器间的效率比拼显得更为复杂。
脱离指令集,在更高层级,比如苹果M1 Max/Ultra的GPU堆料如此充沛,对优化好的应用有爆发力十足的性能和能效表现。但同时由于苹果GPU生态的发展水平不怎么样,致大部分应用跑在M1的GPU上效率都很糟,连一半的能力都发挥不出来。前不久新西兰的一位数据科学家发了一篇文章谈苹果GPU的问题,提到天生的TBDR架构,以及32MB TLB配置,令大部分未能充分利用苹果GPU架构特点的应用跑起来堪称灾难。
这和指令集的关联已经不是那么大了,但生态发展水平对各层级效率的影响显然是需要纳入考量的。
关键并不在指令集本身
去年Jim Keller在接受AnandTech采访时说,“争论指令集是很悲剧的事情”,“核心执行的很大一部分工作就6个指令:加载、存储、加、减、比较、分支”,“指令集本身真的关系没那么大”。他也提到了有关变长指令之类的问题讨论,强调了变长指令问题并不大,尤其在有好的指令预测机制的情况下。
对处理器性能、功耗、能效真正产生影响的是前端如何喂饱后端,cache设计、分支预测、数据预取这些微架构实施层面的问题。Jim Keller就特别谈到了现在限制计算机性能的关键在可预测性上,包括指令/分支预测和数据本地性问题。
十多年前陆续就有向HPC系统引入低功耗处理器核心的研究,都是忌惮于HPC越来越控制不住的功耗。而此间指令集与功耗相关性的研究更是不少。比如Energy Efficiency Evaluation of Multi-level Parallelism on Low Power Processors – 这份2014年的paper着重考察了Intel Atom处理器(Bonnell核心)与Arm Cortex-A9核心的性能和功耗。测试中Bonnell的表现是在性能和能效方面都碾压彼时的Cortex-A9的。当然这只是孤证。
更早的Power Struggles: Revisiting the RISC vs. CISC Debate on Contemporary ARM and x86 Architectures研究则明确Arm和x86不同处理器在功耗和性能方面的差异主要源自设计目标差异,指令集本身从来不是什么重要决定因素。“指令集的差异,虽然可能会产生最终芯片设计实施方案上的影响,但现代微架构技术几乎消除了这种差异;并不存在某一种指令集从根本上更加高效。”