先量測別猜測(先剖析再最佳化)
想像你的程式感覺很慢,而你很確定知道原因——一定是那個大迴圈,或那個遞迴函式。於是你花了一天把它重寫,結果程式跟之前一樣慢。真正的成本完全在別的地方。這就是「先量測別猜測」這條規則要防範的日常陷阱:不要相信你對時間花在哪裡的直覺,去查清楚。
具體來說這條規則是:在你為了加速而改動任何程式碼之前,先跑一個工具告訴你時間真正花在哪裡,接著改動量測說很貴的那部分,然後再量一次確認改動真的有幫助。那個告訴你時間花在哪的工具叫剖析器(profiler),這個活動叫剖析(profiling)。你的直覺之所以不可靠,是因為現代 CPU、它的快取、記憶體系統與作業系統交互作用的方式,沒有人能在腦中追得清楚;那行看起來無害的程式碼(一次陣列存取)可能正是等記憶體等了數百個週期的那一行,而那看起來嚇人的算術其實幾乎免費。
它之所以重要,是因為最佳化本身就很貴——它花你的時間,而且通常讓程式碼更難讀、更容易有 bug——所以你只想把力氣花在划算的地方。一個常見的專業紀律是:先做對,再量測,然後只最佳化剖析器顯示主宰執行時間的那一小部分程式碼(常稱為熱路徑,hot path)。誠實的提醒:量得不好比不量更糟,因為一個誤導的數字給你錯誤的信心——所以這條規則其實是「量得好」,而這個領域接下來談的就是這件事。
$ perf record ./myprog # 收集剖析資料 $ perf report # 78% 的時間在 parse_line(),你懷疑的迴圈只佔 2% # 結論:去最佳化 parse_line(),而不是那個迴圈
剖析器推翻了猜測:被懷疑的迴圈只佔執行時間的 2%,而沒人擔心的剖析函式佔了 78%。
高德納(Knuth)那句名言「過早最佳化是萬惡之源」是同一個意思——但它有下半句:他說一旦你靠量測找出關鍵的那 3%,就要去最佳化它,而不是永遠不最佳化。