我們到底在量什麼
到這裡,第 1 篇的效能鐵律應該已經刻進你骨子裡了:一個程式的執行時間 = 指令數 x CPI x 週期時間,而每一層——編譯器、ISA、微架構、矽——各自撥動其中一個因子。所以最乾淨的效能度量殘酷地簡單:那個真正的程式,從頭到尾,牆上的時鐘走了多久?比這更花俏的都是捷徑,而每條捷徑都是一個騙自己的地方。
第一個岔路是延遲對吞吐量,這也是第 1 篇畫過的。延遲是一個任務從請求到結果花多久;吞吐量是每秒能完成多少任務。它們是不同的問題,一個改動可能幫了這個卻傷了那個。加深管線會抬高吞吐量,卻給每條指令添上一點延遲;把網頁請求成批處理會抬高吞吐量,卻讓任一使用者等更久。在你報出單一數字之前,先想清楚你真正付錢買的是哪一個——影片編碼器靠吞吐量過活,遊戲的輸入迴圈靠延遲過活。
為什麼 MIPS 和 FLOPS 會騙你
用原始速度替 CPU 評分很誘人:MIPS(每秒百萬指令)或 FLOPS(每秒浮點運算數)。兩者聽起來都很客觀,也都可能嚴重誤導。MIPS = 指令數 / (執行時間 x 10^6),也就是 MIPS = 時脈 / (CPI x 10^6)。盯著它看,麻煩就跳出來了:它獎勵「每秒執行更多指令」,但一個程式變快是因為它「整體執行更少指令」。一台 CISC 機器和一台 RISC 機器做同一件事,可能報出天差地別的 MIPS 數字,卻在相同時間完工——因為它們需要不同的指令數。MIPS 量的是力氣,不是結果。
這個陷阱還有更陰險的一面。假設某個聰明的編譯器最佳化刪掉了多餘的工作,程式因此快了 20%——但被刪掉的偏偏是便宜、高 MIPS 的指令。執行時間下降了,MIPS 分數卻也下降了,因為剩下活著的都是慢指令。用 MIPS 這把尺,較快的那台機器反而看起來更差。以前有人開玩笑說 MIPS 是「Meaningless Indicator of Processor Speed(無意義的處理器速度指標)」,而他們不全是在開玩笑。
FLOPS 穿著實驗袍,得的是同一種病。它在一個狹窄的世界裡確實有用——稠密線性代數,那裡浮點運算真的就是正事——但替整台機器報「峰值 FLOPS」,通常指的是沒人達得到的理論最大值:每個浮點單元每個週期都忙著、沒有停頓、沒有在等記憶體。真實程式遠遠落在峰值之下,而這個落差本身就是關於架構的一個事實(你之後會遇到的屋頂線模型正是為了揭露它而存在)。峰值 FLOPS 告訴你機器不可能超過多少,不是你的程式拿得到多少。
真實的基準,與不作弊的那種平均
如果單一數字會說謊,誠實的答案就是跑一整套真實程式,並把它們全部報出來。這就是基準測試的意義——而業界標準的合集是 SPEC,一組精選的真實應用(編譯器、影片、物理、AI),廠商在嚴格規則下跑,好讓結果可比。一整套有抗作弊的本事:要同時對十幾個性質各異的程式各個下特例很難,而且一旦編譯器學會在某個基準上作弊,SPEC 委員會就會把它退役。但一整套會給你一堆數字,遲早有人想要一個總結。陷阱就在這裡彈出來。
假設你把每個程式量成一個 加速比——機器 A 比參考機快幾倍。要做總結,最直覺的動作是算術平均(全加起來除以個數)。對比值來說它是錯的,而且錯得危險:比值的算術平均取決於你挑了哪台機器當基準。把參考機一換,誰「贏」就可能翻盤。一個換掉分母就改判的總結根本不是總結;它是一枚你能灌鉛的硬幣。
Two programs, two machines. Speedup of A relative to baseline B:
prog 1 prog 2 arithmetic mean geometric mean
A vs B : 4.0 0.5 2.25 sqrt(4.0*0.5)=1.41
B vs A : 0.25 2.0 1.125 sqrt(0.25*2.0)=0.71
Arithmetic mean: A looks 2.25x, B looks 1.125x -> who is faster
flips with the baseline. Geometric means are reciprocals
(1.41 vs 0.71), so the verdict is stable: A is sqrt(2)~1.41x B.解法是幾何平均:把這 n 個比值相乘,再開 n 次方。它具備一個比值總結非有不可的性質——不管哪台機器當基準,結果都一致,因為「A 比 B」的幾何平均,恰好是「B 比 A」幾何平均的倒數。這正是 SPEC 為什麼回報「正規化加速比的幾何平均」。這不是品味問題;它是唯一一種拒絕讓參考機的選擇來操控答案的平均。
Amdahl 定律:每個加速的天花板
現在假設你讓程式的某一部分變快了——一個新的向量單元、更多核心、一個手調的熱迴圈。整個程式加速了多少?答案很令人謙卑,它叫做 Amdahl 定律。如果有一部分 f(佔執行時間的比例)被加速了 s 倍,而剩下的 (1 - f) 原封不動,那麼整體加速比 = 1 / ((1 - f) + f/s)。整個程式永遠快不過「你沒改進的那部分」所允許的程度。
把 s 推到無限大——讓被改進的部分變成瞬間完成——加速比的上限就停在 1 / (1 - f)。所以若一個程式有 90% 能完美平行、但 10% 仍是串列,就算用無限核心,你頂多拿到 1 / 0.1 = 10 倍,再多也沒有。砸一千個核心進去,你還是被卡在 10 倍左右,卻得付一千個核心的錢。這就是前面幾階那句警告背後冷冰冰的算術:更多核心很少意味著等比例的加速,因為 Amdahl 定律讓那塊頑固的串列比例當了家。
功率、能量,與挑選真正重要的那個指標
速度只是故事的一半;接下來兩篇會把功率擺到正中央,所以先在這裡認清這個分別。功率是速率——每秒焦耳,瓦特——它決定晶片跑得多燙、散熱器得多大。能量是總量——功率對時間的積分,焦耳——它決定你的電池續航和電費單。兩者會拆開來走:用一半的時脈跑也許能把功率減半,但若工作因此花上兩倍時間,用掉的能量一樣多。手機在乎能量(電池);資料中心兩者都在乎(電費單以及散熱牆)。
因為速度或功率單獨都抓不住「好」,架構師常引用能量延遲乘積(能量 x 時間):它會懲罰一個既浪費或又慢的設計,所以你沒法靠多燒幾瓦去省一毫秒來作弊,也沒法靠龜速爬行去省電來作弊。沒有放諸四海皆準的「最佳」指標——而這正是本篇誠實的核心。對的數字取決於你在為什麼付錢:交易員要延遲,算圖農場要吞吐量,手錶要能量,而一台既要快又要涼的筆電要的是能量延遲乘積。
最後一道防止自欺的護欄。永遠要報出工作負載是什麼,每個測試跑夠多次以看清變異(現代機器很吵——快取要暖機、時脈先衝高再降頻、作業系統會插斷),而且永遠別相信一個你重現不出來的數字。功率之所以硬擠進一個談速度的章節,原因就是 2005 年前後 Dennard 縮放的終結——時脈不再免費變快——而這正是本階最後兩篇要講的故事。