一句話說清這個領域
計算機結構研究的是如何打造一台能把程式跑好的機器——快速、便宜,並且在功耗預算之內。它正好坐落在軟體與硬體的接縫上:在它之上,程式設計師寫程式時不必在意電晶體如何切換;在它之下,工程師接電路時也不必知道將來會跑哪個應用程式。這門學問就是設計那道接縫的藝術,讓兩邊在絕大多數時候都能彼此忽略。如果只記得一件事,請記住:計算機結構從根本上是透過精心挑選的介面來駕馭複雜度。
請注意「跑好」這個詞。一台會給出正確答案、卻要跑上一年、或燒壞、或貴得離譜的機器,即使結果正確,也是糟糕的設計。所以結構設計永遠是在速度、成本、能耗、面積與可靠度之間取得平衡——絕不是只把單一數字最大化。在這條學習階梯中,你會一再看到同樣的張力:一個換來速度的聰明把戲,通常會在別處花掉功率、晶片面積或設計心力。
結構 vs. 組織:許下的承諾與兌現的方式
整個主題裡最重要的一個區別,就是結構與組織之間的分別,而初學者老是把兩者混為一談。指令集架構(ISA)是那份契約:處理器承諾要執行的指令清單、程式看得見的暫存器、記憶體如何定址、每個操作對可見狀態做了什麼。這是寫編譯器的人或寫組合語言的人必須知道的——而且僅此而已。ISA是一個刻在石頭上的承諾;對著它打造的程式,應該能在任何遵守同一份契約的晶片上執行。
計算機組織,又稱微結構,講的是如何兌現那個承諾——也就是隱藏的機械。晶片是一次執行一條指令,還是像生產線一樣同時重疊五條?它有沒有一個放最近用過資料的小快取抽屜?它會不會以與書寫順序不同的次序執行指令,只要可見結果符合契約?這些都不會出現在ISA裡。兩塊晶片可以共用完全相同的ISA(所以同一份二進位檔在兩者上都能跑),組織卻天差地遠——一塊便宜又慢,一塊快又耗電。這正是為什麼軟體廠商出一份程式,就能同時跑在廉價筆電與伺服器上。
儲存程式機器與它的層次
你會遇到的幾乎每一台電腦,都建立在一個大想法上:儲存程式概念。指令與資料住在同一塊記憶體裡,都編碼成數字,所以一個程式不過是機器讀取並服從的一串位元。這是馮諾伊曼模型的核心,我們會在下一篇導覽中深入探討。現在先記住這幅卡通圖:一個CPU(一個決定要做什麼的控制單元,加上一條做算術的資料路徑)、一塊同時存放程式碼與資料的記憶體、一些對外的輸入/輸出,以及在它們之間搬東西的匯流排。那條通往記憶體的共用路徑,正是著名的馮諾伊曼瓶頸——每一條指令與每一筆資料都得擠過的同一道門。
像「把這個串列排序」這樣的高階想法,是怎麼變成電子在流動的?靠的是抽象層,每一層都假裝它下面那一層比實際更簡單。高階語言裡的一個迴圈變成組合語言指令;那些再變成二進位指令流;晶片把每條指令解碼成控制訊號;訊號操控由電晶體搭成的邏輯閘。每一層都把它底下的雜亂藏起來,這正是一個人類有辦法去推理其中任何一層的唯一原因。我們剛認識的ISA,不過就是軟體止步、硬體開始的那一層。
而機器的心跳就是時脈。一切都在時脈邊緣上同步發生,以每一拍搬動多少位元、位元組與字組的資料來衡量。一台電腦跑它的基本迴圈——取下一條指令、解碼它的意思、執行它、重複——不過就是把這套舞步每秒跳上數十億次。我們會在第四篇導覽裡仔細追蹤那個迴圈;在這裡只要注意到它存在,並且時脈速率(每秒幾個週期)只是速度上的三個旋鈕之一,而非故事的全部。
它到底有多快?效能鐵律
這裡正是大多數新手被騙的地方。時脈數字越高並不代表電腦越快——這就是著名的「百萬赫茲迷思」。要誠實地談一個程式跑多久,唯一的辦法是效能鐵律,它把執行時間拆成三個彼此獨立的部分。把它背起來;這條學習階梯的其餘部分都是它的註腳。
CPU time = instruction count x CPI x cycle time
| how many | cycles | seconds
| instructions | per | per
| the program runs| instr. | cycle
CPI = cycles per instruction (lower is better)
cycle time = 1 / clock rate (e.g. 3 GHz -> 1/3 ns)慢慢讀這條鐵律。第一項,指令數,大多由你的程式與編譯器決定。第二項,CPI(每條指令的週期數),是組織居住的地方——管線、快取與聰明的執行方式會把它壓下來。第三項,週期時間,是時脈速率的倒數。陷阱在於,這三者在實務上並非互相獨立:把時脈拉高往往逼出更深的管線,反而抬高CPI,所以把吉赫翻倍,很少能把執行時間砍半。一塊時脈較低但組織更聰明的晶片,可以輕鬆勝出。速度是三個因子的乘積,而業務員最愛只報出最響亮的那一個。
為什麼硬體不再自己變快
幾十年來,程式設計師享有免費的午餐:等個兩年,買一塊新晶片,同一份程式碼不費吹灰之力就跑得更快。這頓午餐來自兩個趨勢。摩爾定律觀察到一塊晶片上的電晶體數量大約每兩年翻一倍,給了設計者更多積木。同樣關鍵的,丹納德縮放指出,當電晶體縮小時,它們的功率密度保持不變——所以你可以把更小的電晶體推得更快,而晶片不會過熱。兩者合力,讓時脈速率攀升了三十年。
接著大約在2005年,丹納德縮放實質上終結了。電晶體繼續縮小,但漏電與功率密度不再配合,於是把時脈拉得更高就只會把晶片燒熔——這就是功率牆。摩爾定律本身也在放緩,電晶體不再免費或輕鬆。免費午餐結束了:單一核心無法只是一味地不斷變快。
這一個事實,幾乎解釋了所有現代的東西。既然無法讓單一核心變快,設計者就把多出來的電晶體花在更多核心上(多核心),以及花在能高效完成狹窄工作的專用引擎上(GPU、張量單元、加速器)。問題在於負擔轉移到了軟體:平行加速由Amdahl定律支配,我們稍後會遇到它——更多核心很少意味著按比例變快,因為程式中頑固的序列部分會封頂整體增益。所以這條學習階梯的其餘部分,某種意義上,就是在講:當時脈不再上升,結構設計師如何持續交付效能——以及為什麼你,這位程式設計師,越來越得出手相助。
這個領域不是什麼
現在先清掉幾個迷思,免得它們稍後絆倒你。結構不等於組織——ISA契約是穩定的,而它底下的廚房每一代都被重新發明。光是時脈較高並不代表比較快;鐵律有三個因子,不是一個。「RISC vs CISC」也不再是一場乾淨的對決,因為現代x86晶片在內部悄悄把它們的複雜指令拆成類似RISC的微指令,所以可見的ISA與隱藏的執行方式是兩回事。
- 結構是契約(ISA);組織/微結構是實作。同一份契約,可以有很多種廚房。
- 更快的時脈只是三個旋鈕之一(指令數 x CPI x 週期時間)。要用真實程式的執行時間來判斷,絕不要看百萬赫茲。
- 更多電晶體(摩爾定律)與每單位面積功率不變(丹納德縮放)帶來了免費午餐——但丹納德在2005年左右終結,於是單一核心不再自己變快。
- 那場終結正是多核心與加速器(GPU、張量單元)存在的原因——這個領域從拉高時脈,轉向以不同方式花用電晶體。
把這些區別一路帶上階梯。在後面的導覽裡,我們會打開馮諾伊曼這個盒子、沿著抽象層一路下到邏輯閘與電晶體、親手追蹤取指—解碼—執行的迴圈,並釘住「效能」誠實的意思。後面每一個把戲——快取、管線、亂序執行、虛擬記憶體、多核心——都不過是對鐵律不斷追問的同一個問題所給出的一個巧妙答案:我們要如何在不破壞契約的前提下,讓常見情況跑得快?