首頁 > 俗語

坑苦了作業系統的x86處理器到底哪裡難做?

作者:由 EETOP 發表于 俗語日期:2022-08-07

多少沾點nt怎麼回覆

相信各位一直會有相同疑惑:為何今天的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 問世的時機,卻是眾多歷史機運的集大成:

「x86 處理器走向高效能化」。

「x86 開始與RISC 正面競爭」。

「x86 指令集的缺陷讓廠商感到棘手」。

「x86 處理器進軍多處理器平臺」。

「個人電腦市場因Windows 95 的問世而蓬勃成長」。

「膝上型電腦即將逐漸普及」。

「英特爾默默埋下讓擅長改良的以色列海法研發團隊,主導x86 處理器技術發展」。

「x86指令集的相容性,成為其他競爭者的潛在障礙,也造成軟體開發商的困擾」。

這些因素交錯,奠定了之後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處理器到底哪裡難做?

說到指令集架構如何影響處理器微架構的設計,這幾年來最有名的例項,就是蘋果利用充分掌握封閉平臺的優勢在「最短時間內驅逐32位元應用程式」,掌握ARM指令集邁向64位元的過程帶來的改革與機會,讓A7之後的自家ARM處理器,徹底為64位元的ARMv8-A量身訂做,研發出一系列能同時有效處理更多指令的先進微架構。

所謂的CISC(複雜指令集電腦)出自儲存器容量稀少、缺乏成熟的高階語言編譯器、大多數人使用語言撰寫程式的時空背景,程式設計者寄望直接用透過微碼(Microcode)組成功能強大的單一指令,像十進位數字和複雜字串處理等,撰寫應用程式。

相較之下,RISC(精簡指令集電腦)則是在「萬事皆備」的環境,追求更快更便宜的電腦,將電晶體預算砸在「最經常被使用的簡單指令和運算元定址模式」的刀口上,儘量用硬體線路去取代微碼,並藉由強制僅載入(Load)/儲存(Store)指令可存取儲存器,搭配大型化的資料暫存器與快取儲存器,填補處理器和儲存器之間越來越大的效率鴻溝。

坑苦了作業系統的x86處理器到底哪裡難做?

時過境遷,在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處理器到底哪裡難做?

從這短短一段話,可看到幾個重點:

「毫無道理可循」(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處理器到底哪裡難做?

「讓人感到極度痛苦的定址模式」(extremelypainful addressing scheme):定址模式講得白話一點是「存取所需運算元(運算的目標,例如某個暫存器或某個儲存器位址)的方式」,歷經多年疊床架屋的x86定址模式,尤其是惡名昭彰的節區儲存器(Segmentation),不只加重產生有效位址的工作負荷,也激增整數邏輯運算(ALU)和控制單元的複雜度,「副作用」跟第一項「毫無道理可尋」可謂不相上下。

坑苦了作業系統的x86處理器到底哪裡難做?

這些燙手山竽,就是1980 年代末期和1990 年代初期,眾多企圖研製高效能x86 處理器的有志之士,包含不小心製造出這些「炸彈」的英特爾,不得不面對並設法克服的挑戰,身為x86 世界首顆「超標量(Superscalar)流水線」處理器的Pentium,則是第一個提出的「有效解決方案」,英特爾為此付出了不小代價。

一個時鐘週期執行一個以上指令的「超標量流水線」

近似近代工業生產線的概念,讓所有人都「閒閒有事做」的流水線化(Pipeline)一直是提高效率的最基本手段,80386 的預先指令擷取(Prefetch)已有雛型,而80486 是x86世界首款真正達成指令流水線化的處理器,為了確保儲存器跟得上運算需求,也配置指令/資料共用的第一階快取儲存器,雖然真正「每個時鐘週期都可穩定輸出」者,也只限於簡單的整數邏輯運算指令。

坑苦了作業系統的x86處理器到底哪裡難做?

「一個時鐘週期內執行一個以上指令」的超標量流水線(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處理器到底哪裡難做?

更扯的是,意圖搶奪英特爾勢力範圍的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處理器到底哪裡難做?

我們光從初代Pentium(P5)的晶粒圖,即可清楚看到x86 相容性的代價:大型化的快取儲存器(儘管實質容量不高,但內部結構卻出奇複雜)、巨大的指令擷取單元、解碼與複雜指令微碼控制單元(Complex Instruction Support),這也是日後所有高效能x86 處理器的共同特色。

坑苦了作業系統的x86處理器到底哪裡難做?

指令/資料分而治之的第一階快取儲存器:x86指令集因缺乏足夠的資料暫存器,且包含了大量「暫存器/儲存器互通有無」與直接以儲存器為運算目標的指令,不像RISC的「載入/儲存」架構「一次從儲存器抓了大量資料進來,算完後再一次性丟回儲存器」,特別需要強力的儲存器子系統,所以採用指令/資料分開的第一階快取儲存器,確保兩邊足以餵飽個別的需求。而Pentium的指令快取和資料快取,更是各自大有文章。

前面有提及「x86 指令最大長度是17Bytes」,Pentium 第一階指令快取的內部結構,是以兩個最小存取單位16Bytes 區塊,組成單一32Bytes 的快取線(Cache-Line),假若發生最糟糕的情況,17Bytes 長度的指令「橫跨」了兩條32Bytes 快取線,但仍希望一次擷取到指令流水線,那該怎麼辦?Pentium 匯入跨指令線分離式擷取(Split Fetch),可連續讀取橫跨邊界的兩條16Bytes 最小區塊,而指令讀取緩衝區也是4 倍於80486 的128Bytes,以確保擷取指令的效率可「餵飽」兩條流水線的指令解碼器。

坑苦了作業系統的x86處理器到底哪裡難做?

順便對照一下從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 的節區儲存器(Segment),必須強制檢驗每個節區的大小,確保儲存器運算元落在節區描述器所定義的儲存器範圍內。80286 時代的保護模式,節區描述器會影響節區位置與體積的引數,總計有:

32 位元基底值(Base)。

20 位元範圍值(Limit)。

範圍值單位Page 或Byte(前者上限4GB,後者則1MB)。

針對堆疊(Stack)資料結構的向下擴充套件(Expand-Down)欄位。

處理器需採取不同的方式計算最高與最低位址,結果Pentium的兩條指令流水線,為此個別又得「再」加上一套4 個輸入值的加法器(4-Input Segment-Check Adder) ,為檢查節區正確性之用,而486的情況如上述位址產生器,須耗費更多時鐘週期做這件事。

坑苦了作業系統的x86處理器到底哪裡難做?

為何「位址計算單元」一向是歷代x86 處理器增加執行單元的重頭戲,原因就在此。Windows 95 刺激個人電腦普及的年代,「32 位元最佳化」的Pentium Pro 被批評「16 位元效能不佳」就因動到資料節區暫存器的指令,無法被非循序預測執行,會讓隨後的指令上演大塞車,到了Pentium II 才修正。即使假以時日,這些老舊包袱的使用率只會越來越低,也早不再是改善效能的重點,但也沒人膽敢冒著犧牲軟體相容性的風險,根除這些歷史遺蹟。

坑苦了作業系統的x86處理器到底哪裡難做?

正面對決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處理器到底哪裡難做?

80×87 浮點指令集更是x86 處理器追求高效能浮點運算的罩門,因「英特爾內部溝通不良」(英特爾美國加州總部和以色列海法之間實在太遠了,1970 年代末期的聯絡手段又沒像今天這麼方便)誕生的「極度愚蠢」堆疊式(Stack)暫存器架構(附贈讓人摸不著頭緒的80 位元延伸雙倍精確度浮點格式),強迫多數浮點指令的運算元,其中一個非得指定放在堆疊暫存器的頂端不可。

坑苦了作業系統的x86處理器到底哪裡難做?

英特爾在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 指令集的沉重包袱,並未讓英特爾就此停下腳步,依然持續精進Pentium 處理器,就算沒有一鼓作氣開啟天堂大門,卻也讓緊閉已久的門縫滲出充滿希望的曙光。

從企圖殺入很長一段時間內「可遠觀不可褻玩焉」的伺服器市場,預期Windows 95 激發個人電腦市場爆發性成長時,補足高效能桌機和筆記型電腦需要的基本功能,到迎合「多媒體」的新潮技術營銷名詞,無不是英特爾1990 年代初期念茲在茲的技術發展重點。這些努力的痕跡,統統一字不漏深深刻在Pentium和整個計算機工業的歷史上。

原汁原味的多處理器支援性

「多處理器支援性」是進入工作站與伺服器市場的最低門檻入門票,而Pentium 則是x86 歷史上首度「原生支援(Glueless)多處理器」的先行者,但嚴格說來,這到了0。5 um 製程的第二代Pentium(P54C)才實現,而在此之前,也並不是沒有「多處理器x86」的存在,只是需要外掛特製的系統晶片組,或連作業系統都要特殊版本。

一個便於實作的「無需外掛額外晶片」(Glueless)的多處理器(或多核心)環境,需具以下條件:

分配、協調各I/O 周邊裝置存取處理器需求的能力,發出中斷(Interrupt)時,知道該由哪個處理器負責:標準化的中斷處理機制。

快取儲存器資料一致性協定(Cache Coherence Protocol):回寫式(Write-Back)快取儲存器常見的MESI(Modified, Exclusive, Shared, Invalid)協議。

低成本多處理器系統的根基:可讓多處理器共享的系統匯流排。

3 項條件之一,最重要者莫過於第一項。1983 年,17 名因英特爾極具野心的「32 位元微電腦大型主機」iAPX432 計劃失敗而離職的員工,創立的Sequent ComputerSystems(1999 年被IBM 購併,研發高階英特爾處理器的系統晶片組),就曾推出一系列採用80386 與80486 的多處理器產品線,但這些花費不菲的專屬方案,仰賴特製系統晶片組與定製化過的作業系統,才能正確的將系統中斷(System Interupt)傳送到各處理器,多處理器x86 平臺仍缺標準化的中斷處理機制,並非可長可久的解決之道。

坑苦了作業系統的x86處理器到底哪裡難做?

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處理器到底哪裡難做?

英特爾的競爭對手並非沒有替代方案,1996 年上市的Cyrix 6×86 依據OpenPIC規範,支援自家定義的SLiC,但也只有VIA 的Apollo 晶片組對應此規格,基本上有跟沒有一樣,而AMD的x86 多處理器環境,更是要等到1999 年採用Alpha EV6 匯流排、理論上最多支援14 顆處理器的K7了。

坑苦了作業系統的x86處理器到底哪裡難做?

不過初代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處理器到底哪裡難做?

受制於缺陷重重的系統架構,如效率不足的系統匯流排、處理器缺乏非循序儲存器存取能力、處理器共享外部的第二級快取儲存器讓匯流排問題更加雪上加霜等等,並未讓Pentium 在伺服器市場取得重大突破,到了Pentium Pro 面世後才迎刃而解,開啟Xeon 統治伺服器市場之路,那又是另一段截然不同的故事了。

決定處理器核心/執行緒上限的Local APIC 與闖禍的作業系統支援性

處理器核心持續激增的今日,Local APIC 最重要的角色在於決定處理器的核心與執行緒上限。原先最早的APIC 上限是15,2000 年Pentium 4 開始出現的xAPIC(將APIC 的3 位元專屬匯流排直接「融入」系統匯流排的通訊協定,避免APIC 運作時影響儲存器存取效能)增加到255,2008 年Nehalem 的x2APIC更多達4294967295,可視為「無限大」。

假如各位想多學些對親朋好友炫耀的「無用知識」,稍微花點腦筋,牢記一下這3 個數字的由來:

APIC:Pentium和Pentium Pro(與Pentium II、Pentium III、P6核心的Xeon)動用Local APIC的ID暫存器24-27四個位元,16進位的0xF(10進位制的15)用做廣播,所以2 4 1=15。

xAPIC:Pentium 4到Penryn用到Local APIC的ID暫存器24-31八個位元,16進位的0xFF(10進位制的255)用做廣播,所以2 8 1=255。

x2APIC:Nehalem開始使用存於MSR(Model-Specific Register)的32位元x2APIC ID,16進位的0xFFFFFFFF(10進位制的4294967295)用做廣播,所以2 32 1=4294967295。

但帳面上的「理論值」讓人看得很爽是一回事,微軟這些作業系統廠商是否乖乖買單又是另一回事。很不幸的,AMD Zen2 世代EPYC 與Threadripper將單顆處理器的實體核心術/邏輯處理器,

一舉推進到64 核/128緒,就變成微軟Windows 的災難了。

坑苦了作業系統的x86處理器到底哪裡難做?

一臺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處理器到底哪裡難做?

再次同場加映AMD。剛好前陣子Anandtech 有特別報導的EPYC 在Windows 10 發生的災情(儘管這和APIC 沒有關係)。Windows 系統核心會預設64 個CPU組成一個「Windows Processor Group」,當邏輯處理器超過64 個,會將多出餘數包成另一群,像一顆64 核/128 緒的Threadripper,就會變成一顆實體CPU 有「兩包」64 個邏輯處理器。

坑苦了作業系統的x86處理器到底哪裡難做?

但Windows 10 要企業版(Enterprise)才提供此功能,家用版(Home)和專業版(Professional)會將「滿出來的部分」,誤判為佔用另一個處理器腳位的實體CPU,意思就是誤解成「兩顆」處理器的系統,將誤導作業系統的執行緒排程,降低系統效能,這時關掉多執行緒,很可能表現還比較好。

在計算機的世界,任何「看起來很棒」的技術和功能,無不是「軟硬兼備」的成果,當入手頂規的硬體時,也請多多關心手上的軟體環境是否可發揮最高效益。筆者現在都可以猜到花大錢買TR 3990X 的「長輩」急著升級Windows 10 企業版的畫面了。

「讓人比較有感」的指令集擴充套件

如果能讓筆者選擇,其實x86 指令集擴張史中最重要的一幕,絕對是邁向32 位元的80386、虛擬86 模式(Virtual 8086 Mode)與具備分頁表的虛擬儲存器。但事隔多年,印象最深刻的,依舊對個人電腦市場規模爆炸式成長、使用者急速增加的1990 年代,英特爾在Pentium 家族乾的一堆好事,包括極具歷史意義的第一次SIMD 擴張:MMX。

坑苦了作業系統的x86處理器到底哪裡難做?

相信各位不可能不知道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處理器到底哪裡難做?

這些指令集擴張看似微不足道,遠不如那票SIMD(MMX、SSE、SSE2、SSE3、SSE4、AVX、AVX-512)「華麗壯大」,卻也是不可或缺的基本功,同為「高效能處理器不可被分割的一部分」。但Pentium 史上最知名的指令集MMX,就是一場歡樂異常的連續劇了。

坑苦了作業系統的x86處理器到底哪裡難做?

為了MMX 讓Pentium 被迫大興土木、脫胎換骨

Windows 95 帶動個人電腦的多媒體需求,英特爾自然不能免俗,勢必要讓處理器跟「多媒體」沾上邊,試圖推動主機板加掛一顆來自第三方的數位訊號處理器(DSP),專門處理即時性影音應用程式。但因為NSP 的運作模式是獨立於作業系統的化外之民,等於需要作業系統開後門,微軟為此拒絕買單,因此胎死腹中,才出現了MMX。

坑苦了作業系統的x86處理器到底哪裡難做?

打從英特爾在1996 年,丟擲MMX 這從未講清楚說明白的「無意義技術營銷商標」,全名一直眾說紛紜、莫衷一是,還先後出現3 個版本:

MultiMedia eXtension

Multiple Math eXtension

Matrix Math eXtension

名稱怎樣不重要,各位只要記得一件事:為了作業系統相容性,MMX指令集借用x87 浮點運算暫存器(80 位元中的64 位元)的SIMD「整數」運算,這樣就夠了。英特爾定義全新SIMD 暫存器,從Pentium III「Kaimai」的SSE(KNI,Katmai New Instructions)才開始。

坑苦了作業系統的x86處理器到底哪裡難做?

指令集的編碼空間畢竟有限,英特爾要從哪裡擠出這57 個指令的位置?英特爾將腦袋動到「0Fh」開頭的運算碼(Opcode),這卻造成前所未見的麻煩:過去0Fh 的主要用途「當處理器的解碼器收到時,自動將該指令執行流程跳到外掛的輔助處理器」,當初英特爾就靠這招來處理8087 浮點輔助處理器,0Fh 開頭的x86指令都不是什麼「需要追求效率」者,也因此,Pentium 的指令解碼器也沒有特別「關照」它們,意味著難以迅速完成解碼MMX 指令的重責大任。

主導Pentium MMX(P55C)研發的以色列海法團隊,不得不大興土木,將指令流水線深度從五階延長到六階,爭取足夠的指令解碼時間。多這一階並非有害無益,因為執行單元將有更充裕的時間存取資料快取,並縮短電路的關鍵路徑,利於提高時鐘頻率,讓Pentium MMX 最終可到達300MHz,比前代P54C 多出整整50%。

坑苦了作業系統的x86處理器到底哪裡難做?

但延長指令流水線也帶來更嚴重的分支預測錯誤代價,英特爾索性將「流水線深度長達12 階」的第六世代Pentium Pro 搭載的雙層動態分支預測與副程式返回位址緩衝區等先進技術,原封不動的逆向移植到Pentium MMX,亦倍增快取儲存器和資料寫回緩衝區,轉換虛擬和實體儲存器位址的TLB,也強化為可同時處理兩種不同分頁大小的版本,種種改進專案彷彿威而剛,讓吃下藍色小藥丸的Pentium MMX 搖身一變成「5。5 代」x86 處理器。

坑苦了作業系統的x86處理器到底哪裡難做?

為了減少耗電與發熱,英特爾將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處理器到底哪裡難做?

(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 指令集擴充戰爭,唯一從英特爾手上搶下先機的,也只有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)有關?算了,就當作提醒世人吧。