抽象之塔:從一顆電晶體到一個念頭
把一支現代手機握在手裡,你握著的是人類造過最層層疊疊的東西之一。最底層是電晶體——一個微觀的開關,不過是一個電壓把通道打開或關上。把幾顆按正確的圖樣接在一起,你就得到一個邏輯閘,能算出一個位元的布林邏輯:AND、OR、NOT。把邏輯閘疊成加法器與暫存器,再疊成一顆處理器,把幾百億顆蝕刻在一塊積體電路上——在那座塔的某一層,電荷的原始物理開始表現得像*算術*,然後像*程式碼*,再然後像一通與你阿嬤的視訊。電腦架構,研究的正是那座塔裡其中一層至關重要的樓層。
為什麼那一層樓值得擁有自己的學科?因為它是兩個龐大世界必須相遇的樓層。在它之下,住著電路與晶片工程師,他們以電壓、閘延遲與矽面積來思考。在它之上,住著程式設計師,他們以變數、迴圈與函式來思考。兩邊都不想知道對方的細節——一個 JavaScript 開發者永遠不該需要在意一顆電晶體如何切換,而一個晶片設計者也該能徹底重建矽,卻不弄壞任何一支現有的程式。架構,就是那個讓兩件事同時成立的外交層。
ISA:一份沒人被允許違背的合約
你要怎麼讓兩個世界相遇,又不逼任何一方知道另一方?你寫一份合約。在運算裡,那份合約就是指令集架構,簡稱 ISA。它是一個精確、公開的承諾,內容是:*這顆處理器看得懂的指令完整清單在此,每一道究竟做什麼、有哪些暫存器、數字在記憶體裡如何排列。* x86、ARM 與 RISC-V 就是其中最有名的幾個。ISA 刻意只描述行為,從不描述構造——它告訴你機器做*什麼*,卻擺明拒絕說*怎麼做*。
那一份拒絕,正是全部的天才所在。因為 ISA 只凍結了行為,Intel 與 AMD 才能造出南轅北轍、卻都能跑 x86 的晶片;Apple 才能一代又一代地重新設計它的 ARM 核心、把效能翻倍,而你那些幾年前就編譯好的應用程式,原封不動地照跑。合約是雙方唯一達成共識的東西,而它讓底下的硬體能被無止境地重新發明。一道指令本身不過是一個二進位數字,一串晶片去解碼的位元樣式——是那部布林機器詮釋一段碼的一小片。
C source one ISA instruction the actual bits
---------- ------------------- ---------------
z = x + y; ---> ADD R3, R1, R2 ---> 0000000 00010 00001 000 00011 0110011
"add R1 and R2, (a 32-bit RISC-V word the
put result in R3" decoder will pick apart)
The ISA is the middle column: a fixed vocabulary the compiler
targets and the hardware promises to obey. Change the silicon
freely -- just keep honouring this column.擷取、解碼、執行:每顆 CPU 的心跳
把一顆處理器剝到只剩靈魂,你會找到一個簡單到連小孩都跟得上的迴圈,每秒重複數十億次。CPU 持有一個特別的指標——程式計數器(program counter)——存著下一道指令的記憶體位址。然後它永遠做三件事:擷取(fetch)那個位址上的指令、解碼(decode)它好弄清楚它命令什麼、再執行(execute)它——做算術、碰記憶體,或跳到別處去。接著它把程式計數器往前推,再從頭來一遍。這就是「擷取—解碼—執行」迴圈,是運算字面意義上的心跳。
用我們的 ADD 把它變得具體。那道指令以二進位字的形式住在記憶體裡;處理器的資料路徑(datapath)則是那張由導線、邏輯閘與儲存元件組成的網路,負責把它運過那三個步驟。它賴以運算的高速便箋簿,就是暫存器檔(register file)——一小排暫存器,每個存一個字,存取起來遠比主記憶體快。經典的教學設計把這個迴圈切成五個整齊的階段,而追著 ADD 走過它們,是看懂一顆 CPU「思考」最清晰的方式。
- IF——指令擷取。程式計數器指著我們的 ADD;處理器把那個 32 位元的字從指令記憶體裡讀出來,並把計數器加一,指向下一道指令。
- ID——指令解碼。解碼邏輯把位元樣式當成布林欄位來讀:這是一道 ADD、來源是暫存器 R1 與 R2、目的地是 R3。那兩個來源暫存器同時被從暫存器檔裡讀出來。
- EX——執行。那兩個值流進算術邏輯單元(ALU)——一塊由邏輯閘搭成的加法器——把它們相加、產生總和。這裡,才是真正運算發生的地方。
- MEM——記憶體存取。一道單純的 ADD 不碰記憶體,所以它在這一階空轉。(一道載入或儲存指令會在這裡讀寫資料記憶體——這正是這一階存在的理由。)
- WB——寫回。總和被寫回暫存器檔裡的目的暫存器 R3。指令完成;結果現在對任何接下來需要 R3 的指令都可見了。
管線:一間給指令用的自助洗衣店
一次只跑一道指令,你幾乎浪費了一切。當 ALU 在 EX 階段忙著時,擷取指令的硬體乾坐著;當 WB 在寫回結果時,ALU 在打盹。這就像洗衣服時,先洗一桶、完全烘乾、摺好,然後*才*開始下一桶——烘衣機運轉的整段時間,洗衣機都冰冷地閒著。沒人這樣洗衣服。你會在第一桶一移到烘衣機的瞬間,就開始洗第二桶。每台機器都保持忙碌,一疊照順序洗會花上一整天的衣服,只用一小段時間就洗完。
這正是指令管線。把資料路徑切成那五個階段,在每一對之間塞一個小小的暫存器去存住進行中的指令,讓五個階段同時運轉——各自處理*不同*的指令。當 ADD 進入 EX,它後面那道指令進入 ID,而一道全新的進入 IF。指令們步調一致地行進過各階段,每個時脈節拍走一步,就像生產線上的車子。沒有任何單一的 ADD 跑得更快——它仍要五個階段——但現在一道完成的指令*每一個週期就從末端滾出來一道*,而不是每五個週期一道。
Cycle: 1 2 3 4 5 6 7 8 9
+-----------------------------------------------------+
ADD R3 | IF | ID | EX |MEM | WB | |
SUB R6 | | IF | ID | EX |MEM | WB | |
AND R8 | | | IF | ID | EX |MEM | WB | |
OR R9 | | | | IF | ID | EX |MEM | WB | |
XOR R10 | | | | | IF | ID | EX |MEM | WB | |
+-----------------------------------------------------+
^ ^
fill-up steady state: ONE instruction finishes
(latency) every clock cycle (throughput)
5 instructions, no pipeline: 5 x 5 = 25 cycles
5 instructions, pipelined: 9 cycles (~2.8x faster, and
the gap only grows with more)為什麼速度是兩個數字、不是一個:頻率 × IPC
問一個初學者為什麼一顆晶片比另一顆快,答案通常是單一個數字:gigahertz(吉赫)。那只是一半的真相,而缺的那一半,正是現代設計安身立命之處。整條管線跟著一個鼓點行進——時脈訊號,一個以固定頻率上下擺動的方波。每一個節拍,每道指令前進一階。一個 3 GHz 的時脈每秒跳三十億次,所以它的週期只有三分之一奈秒。時脈越快、每秒節拍越多、推過的指令越多。到這裡,都還是 gigahertz 的故事。
但還有第二根槓桿,同樣強大:你*每*一個節拍完成多少道指令。我們這條簡單的管線最好也只能每週期一道——IPC(每週期指令數)等於 1。一顆聰明的超純量(superscalar)處理器每個週期擷取並執行好幾道指令,IPC 達到 4 甚至更高。真正的效能是這兩者的乘積,而一個著名的恆等式把它釘死:程式執行時間 = 指令數 × 每指令週期數 × 每週期時間。晶片設計者可以對這三項全部施力。
Execution time = N_instructions x CPI x T_clock
= N_instructions x CPI / f_clock
where CPI = cycles per instruction (= 1 / IPC)
f_clock = clock frequency
Worked example -- a program of 1 billion instructions:
Chip A: f = 4 GHz, IPC = 1 -> time = 1e9 x 1 / 4e9 = 0.250 s
Chip B: f = 3 GHz, IPC = 2 -> time = 1e9 x 0.5 / 3e9 = 0.167 s
Chip B is SLOWER in gigahertz yet 1.5x FASTER overall --
because it does more work per tick. Frequency is only half
the story; IPC is the other half.這就是為什麼,大約在 2005 年,時脈速度停止了它數十年來那樣的攀升。把頻率催得更高,燒掉功率與熱量的速度,比晶片散得掉的還快——這道牆,後面每一個階段都會回頭再談。於是設計者轉向另一根槓桿:每週期做更多工、更多核心、更聰明的管線、更深的指令級平行性。這些把戲每一個,骨子裡都是一種在不把晶片燒熔的前提下提高 IPC 的方法。你現在已經見過的這台基礎機器——ISA、迴圈、管線、頻率、IPC——正是它們全部要去最佳化的對象。
基礎機器,與前方的路
退一步,看看你現在握有什麼。你能把一行程式碼順著塔往下追:從一個敘述,到一道 ISA 指令,到一個二進位字,到電子在邏輯閘裡翻動電晶體。你能帶著那道指令走過擷取—解碼—執行,看它漣漪般掃過五個資料路徑階段,讀寫暫存器檔,並理解為什麼把那些階段在管線裡重疊起來會把吞吐量倍增。而你能用正確的雙手握法去推理速度:時脈頻率與 IPC 兩者並重。這就是基礎機器——那台簡單、誠實的 CPU,每一個華麗的最佳化都被栓在它身上。