上集建立「及早測量、頻繁測量」的效能工程思維,本集深入 MeasureIt 工具內建基準數據,結合 .NET 運行時指令層級成本模型,示範如何驗證基準數據可信度、解讀統計分佈、預測設計權衡成本,並揭露微基準測試常見陷阱與非 CPU 瓶頸識別方法。
下載 MeasureIt.exe(含原始碼),無參數執行即跑約 50 項核心基準:
MeasureIt
輸出 HTML 報告,每項基準執行 10 次,統計中位數、平均值、標準差、最小/最大值。數值經**標準化**:空靜態方法呼叫中位數 = 1.0 時間單位。
| 操作 | 中位數 | 平均值 | 標準差 | 最小值 | 最大值 | 相對成本 |
|---|---|---|---|---|---|---|
| 空靜態方法呼叫 | 1.000 | 1.103 | 0.496 | 0.857 | 2.577 | 基準 (1x) |
| 新建一般類別實例 | 5.060 | 10.223 | 13.927 | 3.340 | 51.215 | ~5x |
| 新建可終結類別實例 | 78.552 | 155.408 | 168.595 | 64.997 | 629.243 | ~78x |
| Activator.CreateInstance | 102.510 | 102.949 | 4.076 | 96.876 | 109.819 | ~100x |
| 陣列整數索引寫入 | 0.713 | 0.664 | 0.076 | 0.574 | 0.773 | ~0.7x |
| 陣列字串索引寫入 | 3.402 | 3.405 | 0.012 | 3.397 | 3.442 | ~3.4x |
| 委派呼叫 | 1.235 | 1.205 | 0.111 | 1.094 | 1.475 | ~1.2x |
| 反射 MethodInfo.Invoke | 472.283 | 472.744 | 5.409 | 466.291 | 482.094 | ~470x |
| P/Invoke (Full Trust) | 6.184 | 6.254 | 0.793 | 5.469 | 7.599 | ~6x |
| P/Invoke (Partial Trust) | 27.806 | 30.440 | 8.735 | 26.343 | 56.582 | ~28x |
Activator.CreateInstance ~100x,MethodInfo.Invoke ~470x。效能敏感路徑**嚴禁反射**,改用委派快取、原始碼生成或 IL 發射。SuppressUnmanagedCodeSecurity (需極謹慎) 或批次化呼叫。了解 JIT 編譯器輸出的機器指令近似成本,可在無基準環境時快速估算,並驗證基準數據是否合理:
| 操作 | 指令數估算 | 備註 |
|---|---|---|
| 整數運算 | 1 | 單指令,通常 < 1 CPU 週期 |
| 浮點運算 | 1 | x87 指令,SSE2 需 NGEN/硬體支援 |
| 實例欄位讀/寫 (值型別) | 1 | 單指令 |
| 實例欄位寫入 (參考型別) | 6-10 | 需 Write Barrier |
| 靜態欄位讀取 | 1-12 | 共享代碼模式 (ASP.NET) 較貴;NGEN 固定 6 指令 |
| 非虛擬/靜態方法呼叫 | 1 | 單 CALL 指令 |
| 虛擬方法呼叫 | 2 | 虛擬表分派 |
| 介面方法呼叫 | 4-20 | 單目標 4 指令 (Stub);多目標 10-20 (哈希表查找) |
| 委派調度 | 4-15 | 單目標實例 4 指令;靜態目標需參數重排;多播有迴圈 |
| 物件配置 | 10-1,000+ | 一般 10-15;可終結物件更多;含清零記憶體 (與大小成正比) 及 GC 掃描成本 |
| 陣列存取 | 1-25 | 邊界檢查 + 讀取;簡單迴圈 JIT 可消除邊界檢查;參考型別寫入需 Write Barrier + 型別檢查 (~25) |
| 型別轉換 | 4-100+ | 精確型別 4;父類較慢;介面更慢;陣列轉換極慢 |
| 鎖 | 20-1,000+ | 無競爭 ~10-15x CALL/RET;有競爭劇增 |
| P/Invoke | 15-1,000+ | 最佳 15-20 (無安全檢查、無封送);字串/COM 互操作更貴 |
| 反射 | 1,000-10,000+ | 簡單 Type.GetType < 10;其他操作比非反射替代方案貴 10-100x |
| 泛型 | 任意 | 參數為類別時共享代碼 (省記憶體) 但依型別參數操作較慢;結構參數各自獨立代碼 |
驗證技巧:若 MeasureIt 測得「新建類別」中位數 5.06,對應指令模型 ~10-15 指令 + 清零記憶體,合理。若測得 500,極大機率測到了 JIT 編譯、GC 或其他干擾,**必須調試基準代碼找出原因**。
三大場景 CPU 非關鍵:
若應用程式:熱資料結構 < 1 MB、頁面錯誤極少、非高度並行共享資料 → **CPU 密集型**,指令成本模型與 MeasureIt 高度相關。
運行時針對常見情況優化,導致「快路徑」與「慢路徑」巨大落差:
object 參數隱性配置物件,編譯器自動插入,易成隱形熱點。params 陣列:Console.WriteLine 等每次呼叫配置陣列 + 可能裝箱,成本 ~10x 一般呼叫。**結論**:指令模型與基準數據僅供**粗略估算**。熱路徑若可能落入慢路徑,**必須針對實際代碼路徑撰寫微基準實測**,不可盲目信任平均值。