x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(1)

来源:科技新报(台) 作者:痴汉水球

相信各位一直会有相同疑惑:为何今天的x86 处理器市场,台面上只剩下英特尔和AMD 两家美国公司?顶多再加个存在感稀薄的台湾VIA,和少人知悉的俄罗斯Elbrus?对技术有点基础认知的人,多少会直接想到「x86 指令集很复杂很难搞,又有英特尔的授权问题,所以x86 处理器非常不好做」之类的标准答案。

当然,更不乏某些夜郎自大的高论,像「就算x86 指令集再复杂,控制单元也还是很好设计」、「微指令转译早就克服了所有x86 指令集的瓶颈」等。拜托,「做出勉强能动的东西」和「开发出有市场竞争力的产品」完全是天差地别的两件事,要不然你以为英特尔和AMD 天文数字般的产品开发经费,都拿去填海了吗?

总之,高效能x86 处理器之所以难做,混杂了诸多技术和商业因素,暗藏在台面下的细节非外人足以道也。但我们可以回到x86 处理器最重要的历史转捩点:1993年Pentium 处理器,抽丝剥茧一般人难以察觉到的蛛丝马迹。

以「唯偏执狂得以生存」(Only theParanoid Survive)留名于世的英特尔创办人之一安迪·葛洛夫(Andy Grove) 曾经说,2003 年的Centrino 是英特尔的「第二个儿子」与「十年来最重要的品牌」 ,那「第一个儿子」和「十年前最重要的品牌」,想当然尔就是1993 年的Pentium 了。

Pentium 之名起源于希腊文的Penta(意思是「五」),再加上拉丁文的ium 结尾,意指「第五代x86 处理器」,英特尔自此也不再使用80×86 来定义处理器世代(要不然现在还真的讲不出来到底是「几86」)。而Pentium 也在不知不觉中,一步步转型为入门级低价处理器品牌,更不再受限于1993 年的P5,进而横跨历代英特尔的超标量(Superscalar)x86 微架构。

原始Pentium 里的P5 处理器微架构,并没有像后继Pentium Pro 的P6 活得如此之长,影响又如此深远(到2011 年Sandy Bridge 才结束)。但Pentium 问世的时机,却是众多历史机运的集大成:

这些因素交错,奠定了之后25 年技术演进与市场发展的基础逻辑,连时下「AMD 处理器的核心太多,竟然造成Windows 操作系统的大麻烦」,也和Pentium 留下的遗产,多少有些千丝万缕的纠结。

既然连AMD Zen成功的背后,都有这么多不为人知的故事,了解英特尔「Landmark」芯片Pentium的轨迹,将有助跳脱琳琅满目的技术营销名词,重新建立属于自己的「x86处理器世界观」,值得各位细细品味。

x86 指令集先天不足后天失调的原罪

催生「计算机结构」(ComputerArchitecture)一词的「指令集架构」(Instruction Set Architecture),为「电脑的基础语言」与「软硬件中间的接口」(Interface Between Hardware and Software),相同指令集的电脑理应可执行一样软件,具备彼此兼容性,对电脑与处理器微架构(Microarchitecture)的未来发展有举足轻重的影响力。

不同的时空背景,也会产生相异的指令集设计理念,没有绝对的优劣对错──x86算是少见的例外,另一个则是DEC VAX,两者的共同点只有「着毋庸议」的糟糕,英特尔IA-64(Itanium)和DEC Alpha的激烈反动,足以证明连生父都如此厌恶小孩。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(2)

说到指令集架构如何影响处理器微架构的设计,这几年来最有名的实例,就是苹果利用充分掌握封闭平台的优势在「最短时间内驱逐32位元应用程序」,掌握ARM指令集迈向64位元的过程带来的改革与机会,让A7之后的自家ARM处理器,彻底为64位元的ARMv8-A量身订做,研发出一系列能同时有效处理更多指令的先进微架构。

所谓的CISC(复杂指令集电脑)出自存储器容量稀少、缺乏成熟的高级语言编译器、大多数人使用语言撰写程序的时空背景,程序设计者寄望直接用透过微码(Microcode)组成功能强大的单一指令,像十进位数字和复杂字串处理等,撰写应用程序。

相较之下,RISC(精简指令集电脑)则是在「万事皆备」的环境,追求更快更便宜的电脑,将晶体管预算砸在「最经常被使用的简单指令和运算元定址模式」的刀口上,尽量用硬件线路去取代微码,并藉由强制仅载入(Load)/储存(Store)指令可存取存储器,搭配大型化的数据寄存器与快取存储器,填补处理器和存储器之间越来越大的效率鸿沟。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(3)

时过境迁,在x86 主宰云端数据中心、中低端服务器、工作站、桌面机一路到笔电的今日,可能已经没有多少人能回想起「RISC 与CISC 之争」曾发生在1990 年代初期的史迹,但毕竟x86 指令集公认是充满缺陷的产物,连当代最伟大的计算机结构教科书都「白纸黑字」并「烧录」于无数莘莘学子的脑中,而AMD K5 的创造者更是用一句「毫无道理可循」(it just doesn'tmake a lot of sense)一锤定音,几无争议空间。

让人满脸黑线的,只有动辄挥出「逆向思考全垒打」的大恩大德,将x86 的市场胜利,视为「x86 指令集架构优良」佐证的天真论点,并时有所闻,连ARM 都比x86 更「存储器存取密集」(Memory-Intensive)这种话都讲得出口。

我们先来瞧瞧当年AMD K5 的总工程师Mike Johnson 怎么评论x86 指令集,由设计x86 处理器的大师级人物(他本人的确是超标量流水线技术的先驱者,那份变成首本超标量技术专书的史丹佛大学博士论文非常有名,附录更深度分析设计超标量x86 处理器的困难点)写出来的批评,特别有说服力,也一再被后人引述:

The complexity of the x86 is not animpassable barrier. The x86 really isn't all that complex—it just doesn't make a lotof sense…. The biggest weakness in the x86 instructionset is the lack of registers coupled with an extremely painful addressingscheme.

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(4)

从这短短一段话,可看到几个重点:

「毫无道理可循」(doesn't make a lotof sense):相较于指令编码程度统──4 Bytes(32 bits)的多数RISC体系,x86指令集的格式和编码极度混乱,长短粗细肥瘦不一,从1~17 Bytes都有可能。影响所及,遍及所有的环节,从快取存储器撷取指令、实作指令流水线化,到处理器发生中断例外时要迅速储存执行状态并尽快回复等,都造成非常严重的后遗症,激增研制高效能x86处理器的门槛,也增加验证产品的时间与成本。

「缺乏足够的寄存器」(the lack ofregisters):一般RISC指令集都会定义32个通用数据寄存器(讲的精确一点是31个)或浮点运算寄存器,但x86在64位元和AVX之前,怎么样都只有少少的8个(因为要扣掉ESP和EBP,说6个或许比较贴切)。当引进指令流水线化和更先进的超标量流水线后,自然就激增了寄存器冲突的机率,这也是激发x86处理器拥有强大非循序指令执行(寄存器重新更名)与高效率存储器子系统的主因。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(5)

「让人感到极度痛苦的定址模式」(extremelypainful addressing scheme):定址模式讲得白话一点是「存取所需运算元(运算的目标,例如某个寄存器或某个存储器位址)的方式」,历经多年叠床架屋的x86定址模式,尤其是恶名昭彰的节区存储器(Segmentation),不只加重产生有效位址的工作负荷,也激增整数逻辑运算(ALU)和控制单元的复杂度,「副作用」跟第一项「毫无道理可寻」可谓不相上下。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(6)

这些烫手山竽,就是1980 年代末期和1990 年代初期,众多企图研制高效能x86 处理器的有志之士,包含不小心制造出这些「炸弹」的英特尔,不得不面对并设法克服的挑战,身为x86 世界首颗「超标量(Superscalar)流水线」处理器的Pentium,则是第一个提出的「有效解决方案」,英特尔为此付出了不小代价。

一个时钟周期执行一个以上指令的「超标量流水线」

近似近代工业生产线的概念,让所有人都「闲闲有事做」的流水线化(Pipeline)一直是提高效率的最基本手段,80386 的预先指令撷取(Prefetch)已有雏型,而80486 是x86世界首款真正达成指令流水线化的处理器,为了确保存储器跟得上运算需求,也配置指令/数据共用的第一阶快取存储器,虽然真正「每个时钟周期都可稳定输出」者,也只限于简单的整数逻辑运算指令。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(7)

「一个时钟周期内执行一个以上指令」的超标量流水线(Superscalar),早在1965 年发迹于RISC 始祖的CDC6600,1980年代陆续出现在RISC 处理器,如1985 年Power 前身的IBM「America」计划(也是「超标量」一词由来)、1988 年Motorola MC88100、1989 年英特尔i960CA、1990年AMD 29050 等。各位绝对没看错,英特尔、AMD 也做过RISC 处理器。

指令流水线化、超标量流水线、非循序指令执行、大型化快取存储器等常见的效能加速手段,清一色优先降临RISC 处理器的原因,也很简单:RISC 指令集的单纯性与简洁性,让设计者更轻易导入这些技术,并用更短的时间完成产品开发与验证。

1990 年代上半期,一提到高效能处理器,几乎都由RISC 独领风骚,像至今硕果仅存的IBM Power、HP 的PA-RISC、SGI 的MIPS、Sun 的UltraSPARC、Fujitsu 的SPARC64及充满传奇色彩的DEC Alpha 等,这也是「RISC 优于CISC」的理论基础。

但「理论」是一回事,不代表CISC 的x86「实务上」做不到,更何况又是拥有一整支庞大「研发军队」的英特尔,革命性的第五世代x86处理器Pentium 在1993年3 月22 日登上历史舞台,不只要迎击来自AMD、Cyrix、NexGen(被AMD 购并,Nx686 变成K6)的竞争产品(之后还多出Centaur 和Transmeta),更要面对AIM 联盟(Apple、 IBM、Mororola,不是美国的空对空飞弹)PowerPC 的挑战,后者享有威镇四方的「RISC 王者」IBM Power 当强大后盾,市面更不乏「专书」鼓吹PowerPC 相对x86 的优越性,把Pentium批评得一文不值。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(8)

更扯的是,意图抢夺英特尔势力范围的IBM 还开发了「脚位与Pentium 兼容,可以同等效率硬件执行x86 程序码,并兼具32 / 64 位元PowerPC 指令集兼容性」的PowerPC 615,要不是微软可能觉得难搞,不符成本效益,拒绝支持这颗处理器,导致永远无法量产,英特尔的处境会更危险。至于PowerPC 615 取消后,研发团队就投靠Transmeta,接着就无疾而终了。

也许各位难以想像「为什么x86 会受PowerPC 威胁,不是属于不同市场吗」,但此时此刻,没半个正常人会将连服务器市场边都沾不上的x86 和「高效能」三个字联想在一起,甚至连那时候英特尔内部,都很少人愿意相信x86 还有未来性,否则也不会出现IA-64 指令集和Itanium 处理器了。况且微软1993 年7 月发表未来操作系统基础的WindowsNT,打从一开始就同时支持x86、MIPS 和Alpha 三版本,之后还加码PowerPC 和IA-64,「不将鸡蛋放在同一个篮子里」意图太明显了。

换句话说,英特尔当时的战略地位,并不像今天如此牢不可破,更早在1990 年同步启动第六世代Pentium Pro 研发案,完全没有承受失败的本钱与余裕。

天底下没有白吃的午餐

Pentium 是如假包换的x86 世界首款超标量处理器,可在同一个时钟周期内执行最多两个指令,整体结构近似双重流水线的「放大发展版」80486,但也因扛着x86指令集的原罪,由外到内付出了不少代价。爬文至此,建议各位回头复习一次Mike Johnson 评论的3 个重点,你一定会更有感触。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(9)

我们光从初代Pentium(P5)的晶粒图,即可清楚看到x86 兼容性的代价:大型化的快取存储器(尽管实质容量不高,但内部结构却出奇复杂)、巨大的指令撷取单元、解码与复杂指令微码控制单元(Complex Instruction Support),这也是日后所有高效能x86 处理器的共同特色。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(10)

指令/数据分而治之的第一阶快取存储器:x86指令集因缺乏足够的数据寄存器,且包含了大量「寄存器/存储器互通有无」与直接以存储器为运算目标的指令,不像RISC的「载入/储存」架构「一次从存储器抓了大量数据进来,算完后再一次性丢回存储器」,特别需要强力的存储器子系统,所以采用指令/数据分开的第一阶快取存储器,确保两边足以喂饱个别的需求。而Pentium的指令快取和数据快取,更是各自大有文章。

前面有提及「x86 指令最大长度是17Bytes」,Pentium 第一阶指令快取的内部结构,是以两个最小存取单位16Bytes 区块,组成单一32Bytes 的快取线(Cache-Line),假若发生最糟糕的情况,17Bytes 长度的指令「横跨」了两条32Bytes 快取线,但仍希望一次撷取到指令流水线,那该怎么办?Pentium 导入跨指令线分离式撷取(Split Fetch),可连续读取横跨边界的两条16Bytes 最小区块,而指令读取缓冲区也是4 倍于80486 的128Bytes,以确保撷取指令的效率可「喂饱」两条流水线的指令解码器。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(11)

顺便对照一下从NexGen Nx686 发展而来的AMD K6。AMD 取消了两倍核心时钟频率的第一阶快取存储器,但除了既有的预先解码位元(Pre-decoded Bits)用来标定指令边界,再追加第二个指令存取埠,以应付这种状况。反正各家厂商都各显神通,直到可降低指令解码器使用率的微指令快取(uOp cache)同时成为英特尔和AMD 的制式武装为止。

数据快取亦不遑多让,数据快取具备3 个存取埠,可同时应付来自快取数据一致性协定与双重执行流水线的需要,更进一步将每条32Bytes 快取线,切成彼此交错的8 个独立4Bytes「Bank」,只要两个数据存取需求不会同时使用同一个Bank,即可在单一时钟周期内搞定。这是计算机工业史上的第一次尝试,但这也大幅加重了快取存储器的复杂度,也连带不得不强化配置数据快取的虚拟/实体位址转换缓冲区(TLB,Translation-Lookaside Buffer ),并新增判断Bank 是否发生冲突的功能电路。

巨大的微程序只读存储器:基于「加速常用的简单指令」的理念,Pentium 的指令解码器可直接硬体解码大多数「寄存器→存储器」与「存储器→寄存器」之类的「相对简单」运算指令,但 x86 历代累积下来的庞大复杂指令,还是需要动用微码组成微程序产生控制讯号。

Pentium 的单一微码字元长度是 92Bits,总共存放了 4K 数量。换言之,产生了高达 47kB 容量的只读存储器(ROM)空间,还远多于第一级快取记忆体的容量(8kB+8kB),相当惊人,老旧指令兼容性带来的巨大负担,由此可见一斑,这就是维持回溯兼容性,所必须付出的昂贵代价。

4 个输入的位址计算单元:一套所谓「复杂」的指令集,除了不规则的指令编码长度,乱无章法的运算元定址模式和存储器定址,更是必备的条件(可回顾一下 AMD Mike Johnson 讲过的话)。实现高效能的超标量流水线 x86 处理器,并非只需弄好流水线前端的指令撷取、解码,与执行阶段的存取存储器,高效率的有效位址计算(Address Calculation)能力,更是 x86 有别于 RISC 体系的一大差异点,坊间人云亦云、积非成是的「x86 处理器只有指令解码器比较难做」完全是大错特错的误解。

为了加速存储器位址计算,让执行单位尽快得到「运算目标」,Pentium 的两条指令流水线个别有一套 4 个输入值的加法器(4-Input Address Adder),对应 x86 指令集产生有效位址的 4 个数字:

节区描述器(Segment Descriptor)提供的基底值(Base)。

来自通用寄存器的基底位址(Base Address)。

取自通用寄存器的索引值(Index,再加上scale)。

指令编码附上的的移位值(Displacement)。

因此部分仅支持 3 个输出值的 486,需耗费两个时钟周期完成位址计算的复杂指令,Pentium 只需一个时钟周期週期即可,更利于流水线化执行指令。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(12)

但惨剧尚未划下句点,x86 的节区存储器(Segment),必须强制检验每个节区的大小,确保存储器运算元落在节区描述器所定义的存储器范围内。80286 时代的保护模式,节区描述器会影响节区位置与体积的参数,总计有:

处理器需采取不同的方式计算最高与最低位址,结果Pentium的两条指令流水线,为此个别又得「再」加上一套4 个输入值的加法器(4-Input Segment-Check Adder) ,为检查节区正确性之用,而486的情况如上述位址产生器,须耗费更多时钟周期做这件事。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(13)

为何「位址计算单元」一向是历代x86 处理器增加执行单元的重头戏,原因就在此。Windows 95 刺激个人电脑普及的年代,「32 位元最佳化」的Pentium Pro 被批评「16 位元效能不佳」就因动到数据节区寄存器的指令,无法被非循序预测执行,会让随后的指令上演大塞车,到了Pentium II 才修正。即使假以时日,这些老旧包袱的使用率只会越来越低,也早不再是改善效能的重点,但也没人胆敢冒着牺牲软件兼容性的风险,根除这些历史遗迹。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(14)

正面对决PowerPC 且落居下风

相较于同时期且较早推出的超标量RISC 处理器,就可明显看出x86 指令集的复杂度造成的负面影响。

以1993 年秋季上市的IBM PowerPC 601 为例,晶体管数目仅280 万,采用0.6um(600nm)制程时的晶粒面积仅121 平方公厘,却有比晶体管310 万的Pentium 更高的80MHz时钟频率、更大一倍的32kB 指令/数据共用式第一阶快取存储器,与1.5 倍的指令执行能力,而采用0.8um(800nm)制程的初代Pentium是一颗16.7×17.6mm、294平方公厘的巨大芯片,完全瞠乎其后。英特尔当时就表示,相较于同等级RISC 处理器,Pentium 有约30% 晶体管都「贡献」给x86 指令集的兼容性。不难想见那时候的「RISC 十字军」有多high。

而Pentium 的双重超标量指令流水线也是限制重重,只有主流水线「U Pipe」可以执行所有的x86 指令,副流水线「V Pipe」仅能负责比较简单者,而要这两条流水线一起动,还需要依循指令配对规则,讲白了就是「两边都要跑简单指令」,且涉及寄存器和存储器两边数据互相搬移的指令也无能为力,就是要强迫其中一条流水线「发呆」两个时钟周期给你看。PowerPC 601「理所当然」比较没有这样的烦恼。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(15)

80×87 浮点指令集更是x86 处理器追求高效能浮点运算的罩门,因「英特尔内部沟通不良」(英特尔美国加州总部和以色列海法之间实在太远了,1970 年代末期的联络手段又没像今天这么方便)诞生的「极度愚蠢」堆叠式(Stack)寄存器架构(附赠让人摸不着头绪的80 位元延伸双倍精确度浮点格式),强迫多数浮点指令的运算元,其中一个非得指定放在堆叠寄存器的顶端不可。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(16)

英特尔在Pentium 加入FXCH 指令用来交换置顶寄存器,原本仅内建一组浮点运算单元,流水线不能同时执行两个浮点运算指令的Pentium,简单的浮点运算指令可和FXCH 一同塞进两条指令流水线,但实际上也只有执行一个有效浮点运算,况且后头接连着的整数指令,都会被延误最少一个时钟周期。

判断分支条件需「借用」整数运算通用寄存器与执行单元,则是80×87 另一个弱点,从一个浮点运算设定条件码、将浮点运算的执行资讯搬移至通用寄存器、传送至条件码寄存器,再依据其结果,启动正常的分支处理流程,Pentium 整整耗时9 个时钟周期。当然可透过「插入」其他整数指令来降低效能损失,但无法弥补当执行条件判断密集的程序,整数浮点单元之间反覆「踢皮球」的伤害。

这些在今天只会让人觉得很荒谬的往事,让Pentium 的浮点性能仍远远不及同时期的RISC 处理器,只能在x86 的世界当大王,这宿疾到了新一代Pentium Pro 依旧无解,同期MIPS R10000 的SPECfp92 浮点效能还是Pentium Pro 的「3 倍」以上,还因为PowerPC「外挂」AltiVec 而一度被拉开差距到差点看不见车尾灯的程度。

直到Pentium III(Katmai)开始扩充SIMD(单一指令,多重数据流)浮点指令集SSE、初代Pentium 4(Willamette)的SSE2 新增双倍精确度浮点格式,一路到Sandy Bridge 的AVX,引入VEX (VectorExtension)标头,一口气解放了过去x86 指令编码带来的重重枷锁,才算功德圆满。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(17)

但即使看似出师不利,x86 指令集的沉重包袱,并未让英特尔就此停下脚步,依然持续精进Pentium 处理器,就算没有一鼓作气打开天堂大门,却也让紧闭已久的门缝渗出充满希望的曙光。

从企图杀入很长一段时间内「可远观不可亵玩焉」的服务器市场,预期Windows 95 激发个人电脑市场爆发性成长时,补足高效能桌机和笔记型电脑需要的基本功能,到迎合「多媒体」的新潮技术营销名词,无不是英特尔1990 年代初期念兹在兹的技术发展重点。这些努力的痕迹,统统一字不漏深深刻在Pentium和整个计算机工业的历史上。

原汁原味的多处理器支持性

「多处理器支持性」是进入工作站与服务器市场的最低门槛入门票,而Pentium 则是x86 历史上首度「原生支持(Glueless)多处理器」的先行者,但严格说来,这到了0.5 um 制程的第二代Pentium(P54C)才实现,而在此之前,也并不是没有「多处理器x86」的存在,只是需要外挂特制的系统芯片组,或连操作系统都要特殊版本。

一个便于实作的「无需外挂额外芯片」(Glueless)的多处理器(或多核心)环境,需具以下条件:

3 项条件之一,最重要者莫过于第一项。1983 年,17 名因英特尔极具野心的「32 位元微电脑大型主机」iAPX432 计划失败而离职的员工,创立的Sequent ComputerSystems(1999 年被IBM 购并,研发高阶英特尔处理器的系统芯片组),就曾推出一系列采用80386 与80486 的多处理器产品线,但这些花费不菲的专属方案,仰赖特制系统芯片组与定制化过的操作系统,才能正确的将系统中断(System Interupt)传送到各处理器,多处理器x86 平台仍缺标准化的中断处理机制,并非可长可久的解决之道。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(18)

1993 年10 月27 日,也是初代Pentium 发表后的半年,英特尔首度公开第一版「多处理器规范」(MPS,Multi-Processor Specification)与最重要的「处理器本地端先进可程序化中断控制器」(Local APIC,Local Advanced ProgrammableInterrupt Controller)与I/O 专属的I/OAPIC,取代老旧的8259 PIC。

每个Pentium 或80486 处理器起码要有一个Local APIC,与系统I/O 芯片组的I/O APIC,透过独立于系统总线的3 位元APIC Bus,I/OAPIC 将周边装置的中断需求传递给处理器的Local APIC,以决定中断服务需求该指派给那些处理器,踏出了低成本多x86 处理器系统的第一步。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(19)

英特尔的竞争对手并非没有替代方案,1996 年上市的Cyrix 6×86 依据OpenPIC规范,支持自家定义的SLiC,但也只有VIA 的Apollo 芯片组对应此规格,基本上有跟没有一样,而AMD的x86 多处理器环境,更是要等到1999 年采用Alpha EV6 总线、理论上最多支持14 颗处理器的K7了。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(20)

不过初代Pentium 并未内建Local APIC,同时期系统芯片组也没有I/O APIC,要打造多颗Pentium 平台,每一颗Pentium 需外挂一颗单价高达26 美元、兼具Local APIC 与I/OAPIC 两者功能的82498DX,I/O 也需动用一颗。换句话说,双处理器系统就需要用到3 颗,怎么看都不算便宜,还会占用不少主机板空间。

后来0.5um 制程的第二代Pentium(P54C)变成史上第一颗整合Local APIC 的x86 处理器,对应的系统芯片组也陆续在南桥(South Bridge)内建I/O APIC(未内建者,可选配专用的82093AA I/O APIC),总算让双Pentium 摇身一变,成为「Glueless」的多处理器平台。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(21)

受制于缺陷重重的系统架构,如效率不足的系统总线、处理器缺乏非循序存储器存取能力、处理器共享外部的第二级快取存储器让总线问题更加雪上加霜等等,并未让Pentium 在服务器市场取得重大突破,到了Pentium Pro 面世后才迎刃而解,开启Xeon 统治服务器市场之路,那又是另一段截然不同的故事了。

决定处理器核心/线程上限的Local APIC 与闯祸的操作系统支持性

处理器核心持续激增的今日,Local APIC 最重要的角色在于决定处理器的核心与线程上限。原先最早的APIC 上限是15,2000 年Pentium 4 开始出现的xAPIC(将APIC 的3 位元专属总线直接「融入」系统总线的通讯协定,避免APIC 运作时影响存储器存取效能)增加到255,2008 年Nehalem 的x2APIC更多达4294967295,可视为「无限大」。

假如各位想多学些对亲朋好友炫耀的「无用知识」,稍微花点脑筋,牢记一下这3 个数字的由来:

但帐面上的「理论值」让人看得很爽是一回事,微软这些操作系统厂商是否乖乖买单又是另一回事。很不幸的,AMD Zen2 世代EPYC 与Threadripper将单颗处理器的实体核心术/逻辑处理器,一举推进到64 核/128绪,就变成微软Windows 的灾难了。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(22)

一台2 颗EPYC 7742 或7702 的服务器,拥有128 个处理器核心和256 条线程,但是Windows Server 2016 和2012 R2 并不支持「AMD 新型平台的x2APIC」,无法吃下这么多逻辑处理器。

事实上,根据微软的EPYC 性能调校文件,Windows Server 2019 之前的旧版Windows Server,只能支持2 颗48 核心的EPYC 和192 个逻辑处理器。安装2019 年9月前的Windows Server 2019,也需要事先在BIOS关闭x2APIC 和多线程,安装完毕并装完所有的系统更新档,重新开机进BIOS 恢复功能,才能在工作管理员的效能选单看到全部CPU。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(23)

再次同场加映AMD。刚好前阵子Anandtech 有特别报导的EPYC 在Windows 10 发生的灾情(尽管这和APIC 没有关系)。Windows 系统核心会预设64 个CPU组成一个「Windows Processor Group」,当逻辑处理器超过64 个,会将多出余数包成另一群,像一颗64 核/128 绪的Threadripper,就会变成一颗实体CPU 有「两包」64 个逻辑处理器。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(24)

但Windows 10 要企业版(Enterprise)才提供此功能,家用版(Home)和专业版(Professional)会将「满出来的部分」,误判为占用另一个处理器脚位的实体CPU,意思就是误解成「两颗」处理器的系统,将误导操作系统的线程排程,降低系统效能,这时关掉多线程,很可能表现还比较好。

在计算机的世界,任何「看起来很棒」的技术和功能,无不是「软硬兼备」的成果,当入手顶规的硬件时,也请多多关心手上的软件环境是否可发挥最高效益。笔者现在都可以猜到花大钱买TR 3990X 的「长辈」急着升级Windows 10 企业版的画面了。

「让人比较有感」的指令集扩展

如果能让笔者选择,其实x86 指令集扩张史中最重要的一幕,绝对是迈向32 位元的80386、虚拟86 模式(Virtual 8086 Mode)与具备分页表的虚拟存储器。但事隔多年,印象最深刻的,依旧对个人电脑市场规模爆炸式成长、使用者急速增加的1990 年代,英特尔在Pentium 家族干的一堆好事,包括极具历史意义的第一次SIMD 扩张:MMX。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(25)

相信各位不可能不知道CPUID 这经常用来辨识处理器厂牌、功能、版本与规格的好工具,但你们知道背后作这件事的「CPUID」指令,就是从Pentium 开始登场的吗?众多程序设计师计算指令执行周期数的RDTSC(Read Time-Stamp Counter),也伴随着Pentium 而生。用在操作系统避免不同线程同时对共用资源读写「互斥锁」的CMPXCHG8B(Compare and Exchange 8 Bytes),也是小有名气,WindowsXP 就是因这个必备指令,无法执行于Pentium 之前的所有x86处理器。

前面有提到MSR(Model-Specific Register),意指在x86 架构处理器中,一系列用于控制处理器执行、功能开关、除错、追踪程序执行、监测处理器效能等功能的寄存器。MSR 的雏形始于80386 和80486,到了Pentium,英特尔新增RDMSR(ReadMSR)和WRMSR(Write MSR)指令用于读写MSR,使其真正的实用化。此外,软件可透过前述的CPUID 指令,查询处理器可支持的功能,并确认这些功能对应的MSR 是否存在。

同时英特尔在Pentium「复刻」笔电专用的80386SL 和80486SL 处理器,那独立于真实模式和保护模式,干了哪些好事,连操作系统都不知情的系统管理模式(SMM,System Management Mode)。英特尔制定SMM 的初衷,在于让笔电OEM 厂商自订必备的电源管理与周边装置管理,如为了省电,动态关闭用不到的周边设备,需要时再重新启动等,将SMM 从「特殊武器」提拔成「制式装备」,暗示英特尔认定笔电即将普及化的未来。

顺道一题,相对于x86 指令集在节区定址定义4 层权限的Ring 0 到Ring 3(数字越小权力越大),SMM 的权限经常戏称为Ring -2,那Ring -1 跑到哪去了?答案是x86 硬件虚拟化技术用来拦截「在使用者模式仍会更动系统底层的危险指令」的Hypervisor 权限。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(26)

这些指令集扩张看似微不足道,远不如那票SIMD(MMX、SSE、SSE2、SSE3、SSE4、AVX、AVX-512)「华丽壮大」,却也是不可或缺的基本功,同为「高效能处理器不可被分割的一部分」。但Pentium 史上最知名的指令集MMX,就是一场欢乐异常的连续剧了。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(27)

为了MMX 让Pentium 被迫大兴土木、脱胎换骨

Windows 95 带动个人电脑的多媒体需求,英特尔自然不能免俗,势必要让处理器跟「多媒体」沾上边,试图推动主机板加挂一颗来自第三方的数位讯号处理器(DSP),专门处理即时性影音应用程序。但因为NSP 的运作模式是独立于操作系统的化外之民,等于需要操作系统开后门,微软为此拒绝买单,因此胎死腹中,才出现了MMX。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(28)

打从英特尔在1996 年,抛出MMX 这从未讲清楚说明白的「无意义技术营销商标」,全名一直众说纷纭、莫衷一是,还先后出现3 个版本:

名称怎样不重要,各位只要记得一件事:为了操作系统兼容性,MMX指令集借用x87 浮点运算寄存器(80 位元中的64 位元)的SIMD「整数」运算,这样就够了。英特尔定义全新SIMD 寄存器,从Pentium III「Kaimai」的SSE(KNI,Katmai New Instructions)才开始。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(29)

指令集的编码空间毕竟有限,英特尔要从哪里挤出这57 个指令的位置?英特尔将脑袋动到「0Fh」开头的运算码(Opcode),这却造成前所未见的麻烦:过去0Fh 的主要用途「当处理器的解码器收到时,自动将该指令执行流程跳到外挂的辅助处理器」,当初英特尔就靠这招来处理8087 浮点辅助处理器,0Fh 开头的x86指令都不是什么「需要追求效率」者,也因此,Pentium 的指令解码器也没有特别「关照」它们,意味着难以迅速完成解码MMX 指令的重责大任。

主导Pentium MMX(P55C)研发的以色列海法团队,不得不大兴土木,将指令流水线深度从五阶延长到六阶,争取足够的指令解码时间。多这一阶并非有害无益,因为执行单元将有更充裕的时间存取数据快取,并缩短电路的关键路径,利于提高时钟频率,让Pentium MMX 最终可到达300MHz,比前代P54C 多出整整50%。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(30)

但延长指令流水线也带来更严重的分支预测错误代价,英特尔索性将「流水线深度长达12 阶」的第六世代Pentium Pro 搭载的双层动态分支预测与副程序返回位址缓冲区等先进技术,原封不动的逆向移植到Pentium MMX,亦倍增快取存储器和数据写回缓冲区,转换虚拟和实体存储器位址的TLB,也强化为可同时处理两种不同分页大小的版本,种种改进项目仿佛威而刚,让吃下蓝色小药丸的Pentium MMX 摇身一变成「5.5 代」x86 处理器。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(31)

为了减少耗电与发热,英特尔将MMX 执行单元与实体寄存器独立于x87 浮点运算器,执行MMX 指令时,因指令集定义「逻辑上MMX 和x87 浮点无法同时执行」,可关闭「吃电如喝水」般的浮点单元以节约电力,可是结合加倍的快取存储器和种种增强方案,P55C 晶体管数从P54C 的330 万激增到450 万,制程从「不计多出10%芯片面积以追求最高时钟频率」的350 纳米BiCMOS 改进为「自此英特尔转向追求更低成本并降低耗电」的280 纳米CMOS,芯片面积和制造成本仍足足比P54C 多50%。

Pentium MMX 在1997 年1 月上市没多久,同年5 月同样支持MMX的Pentium II 就以「塑胶大弹夹」外观,现身于各地电脑卖场的玻璃柜,无论怎么看,Pentium MMX 都是过渡期强烈的尴尬产物。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(32)

(Source:Flickr/AndyRogers CC BY 2.0)

但Pentium MMX 对英特尔在以色列海法的研发团队而言,却是极为重要的历史里程碑,建立起「擅长精炼现有架构压榨更多价值」的名号,接连重塑P6 微架构成为Centrino 心脏的PentiumM (Banias, Dothan)、当英特尔在Pentium4(NetBurst)惨遭滑铁卢的危急存亡之秋端出Core 2(Merom, Conroe, Woodcrest)救驾成功、融合P6 与NetBurst 之长的Sandy Bridge 终结AMD K8 的辉煌岁月、直到「奋六世之余烈」集大成的「终极x86 微架构」Skylake,清一色都是出自以色列海法团队的不朽杰作。

x86 指令集长期欠缺标准造成竞争对手与软件开发商的困扰

Pentium 的「历史地位」倒是值得另外添一笔:x86 指令集欠缺公开业界标准,搞死不少人的陈年旧帐,终于正式浮上台面。

Pentium Pro 总工程师之一的Robert Colwell 回忆录《The Pentium Chronicles》说过,开发一颗x86 处理器,最艰巨的挑战在于「如何保证可兼容所有旧程序」。特别早期x86 处理器,很多未定义的运算码(Opcode)并没有遮掉,被人发现又拿来用了,以后的处理器开发人员就只能乖乖想办法「塞」进去,前提是你也要知道这些陷阱到底藏在哪里。

各位是否天真的以为所有x86 处理器厂商的产品,都保证彼此兼容,可执行一模一样的软件?很遗憾的,这种好事从来就不存在英特尔统治的x86 世界(最起码,前阵子让Linus Torvalds 大暴走的AVX-512,AMD 现有产品也是付之阙如),英特尔在Pentium 时代的所作所为就是最好例证,让初版使用者手册描述新增指令的「附录H」故意保持完全空白,英特尔的竞争者与软件开发商纷纷变成倒霉的苦主。

英特尔并不像那票会定期推出版本演进与相关规范的RISC 指令集(有关心ARM 的读者应该很清楚)、积极推广自家指令集给其他潜在竞争对手,而是完全不管其他人死活。想研发x86 兼容处理器的有志之士,假若没有跟英特尔签订互相授权协议,只有两条路可选:乖乖用电子显微镜默默研究英特尔处理器的晶粒,尝试逆向工程,要不然就干脆不支持不顾兼容性,碰到就视为非法指令,剩下的烂摊子就丢给操作系统厂商伤透脑筋了。

像Cyrix 在被National Semiconductor 购并前,压根没有英特尔技术授权,只能闷着头逆向工程慢慢搞,自然也无法100% 兼容,让号称「第六世代」的6×86,连CPUID 和RDTSC都残缺不全,指令集兼容水准只有80486 等级,甚至还得逼迫软件厂商撰写修正程序。同时期的AMD 则是不计代价拼死拼活,都要藉由逆向工程挤出100% 兼容性,下场就是产品上市延误,错失商机,还亏当初Compaq 傻傻不肯推出Pentium 个人电脑,宁愿痴痴等待AMD K5,更惨的是,撑到最后还是等不到。

英特尔竞争者也都不是省油的灯,不遑多让抢着跳出来扮演「麻烦制造者」,自行定义「兄弟独有之创见」的自家指令,不仅企图争夺x86 指令集的主导权,并强化产品效能及营销筹码,像AMD K6 的3DNow!、AMD K8 的x86-64,Cyrix 6x86MX 的EMMI 与CyrixIII 的MMX-FP,Centaur 一度想不开的57 个SIMD 浮点指令和22 个自定义浮点寄存器,都是斑斑可考的历史陈迹。

AMD 还一度想不开,抢先注册「SSE5」,摆明跟英特尔AVX 打对台,还好在2009 年5 月6 日紧急采煞车,宣布「皈依」AVX,但还是忍不住捞过界「补完」被英特尔废除的四运算元指令格式(xmm1=xmm2×xmm3+m32)。AMD 要开始支持乱成一团的AVX-512并完全兼容,大概也是很久以后的未来了。

x86处理器有多少:坑苦了操作系统的x86处理器到底哪里难做(33)

回顾这些年来的x86 指令集扩充战争,唯一从英特尔手上抢下先机的,也只有AMD x86-64 那次,还是微软私下威胁「不打算支持两种不同的64 位元x86」(英特尔本来有自己的Yamhill,但为了保护IA-64 迟迟不肯拿出来)强迫英特尔接受的结果。

最初英特尔多心不甘情不愿、打死都不承认 64 位元 x86 存在的「IA-32e」和 AMD x86-64 也并非一模一样,英特尔独占 CMPXCHG16B(Pentium 那个 CMPXCHG8B 的进阶版)和 SSE3,AMD 多出分页表 NX(No Execute)保护位元和 3DNow!。谈到 x86 处理器厂商要彼此 100% 水乳交融,说有多麻烦就有多麻烦,说微软有多火大就有多火大。

不过乱象还是持续延烧,还烧到虚拟化领域,近十多年来虚拟化应用快速普及,然后 2005 年英特尔 VT-x(Vanderpool)和 2006 年 AMD AMD-V(Pacifica),双方根本就是各搞各的,老死不相往来,让 VMware vMotion 此类不停机的虚拟机动态迁移技术,迟迟跨越不了不同 x86处理器厂商的边界,无形中也侷限了 x86 处理器「向上发展」的潜力,目睹此景,IBM 应该会继续开心下去。

重塑x86 处理器世界观的历史认知

行文至此,想必各位看官脸上已挂著颤抖的嘴角与充满劫后馀生的表情,或多或少逐步解构并重组过往对个人电脑市场与 x86 处理器演进史的认知,有可能瞬间茅塞顿开,也有可能继续满头问号。

本文并非教科书,而是经由複习坊间甚少重视的历史背景与不容易注意到的细节,体认到平日陪伺在旁、习以为常的 x86 处理器,能走到今天是多麽不容易的一件事。罗马不是一天造成,英特尔的霸权也不是平白从天上掉下来,这些年来我们一同挤过这麽多条牙膏,更不是毫无苦衷。

身为英特尔 1990 年代「Landmark」芯片与无数潜藏已久历史暗流的缩影,Pentium 带来 x86 世界太多「第一次」,承先启后,奠定 25 年技术演进与市场发展的基础逻辑。毕竟电脑是人类创造的东西,背后的人性和思维远远超越技术。英特尔可靠开创新局的 Pentium 与继往开来的 Pentium Pro,在众多敌人环伺下杀出一条通往全新市场血路过程中「产生的价值」,如「技术领先无法保证商业胜利」和「成功的产品往往是折衷妥协后的产物」,统统很有意思,更值得各位细细品味。

关于Pentium,似乎笔者不小心遗漏了什麽,听说跟浮点除法(FDIV)有关?算了,就当作提醒世人吧。

微信加群

,