延續前文效能測試,本文採用「核武級」手段——直接對比 非泛型(MyArrayList)與 泛型(MyList<object>)在 JIT 編譯後的 x86 機器碼,從 CPU 指令層面一錘定音:兩者執行路徑幾乎完全相同,不存在效能差距。
object[] vs T[]。get/set 標記 [MethodImpl(MethodImplOptions.NoInlining)],強制 JIT 產生獨立方法體,避免內聯干擾觀察。Main 方法先觸發 JIT,再於 Console.ReadLine() 暫停,附加 WinDbg 分析。透過 !do 檢視 Managed Heap 上的物件:
> !do 0205be3c (MyArrayList)
Fields: MT Field Offset Type VT Attr Value Name
5c1b41d0 4000001 4 System.Object[] 0 instance 0205be48 m_items
> !do [MyList<object> 位址]
Fields: ... Offset 4 ... System.Object[] ... m_items
結論:兩者物件佈局完全一致,m_items 均位於物件起始位址 + 4 Bytes,型別皆為 Object[]。泛型實例化為參考型別時,CLR 共享同一套程式碼與結構。
觀察 System.Object[] 記憶體佈局(dd 位址):
反組譯 Main 中對兩者索引器 get_Item 的呼叫片段:
; --- MyArrayList.get_Item(Int32) ---
01d4012d mov ecx, esi ; this 指標
01d4012f xor edx, edx ; 參數 index = 0
01d40131 call dword ptr ds:[343424h] ; 呼叫入口點
; --- MyList`1[object].get_Item(Int32) ---
01d40137 mov ecx, edi ; this 指標
01d40139 xor edx, edx ; 參數 index = 0
01d4013b call dword ptr ds:[343594h] ; 呼叫入口點
指令序列完全同構:參數傳遣(ECX=this, EDX=index)、呼叫慣例、間接呼叫機制無任何差異。唯一不同僅是入口點表位址(對應各自的 MethodDesc)。
從物件佈局、陣列結構、呼叫端機器碼三個維度鐵證如山:泛型在參考型別實例化時,CLR 採用程式碼共享策略,執行效能與手寫 object 版本無異。 任何「泛型較慢」的論調,在 x86 指令面前都不攻自破。
作者提醒:除非必要,勿沉迷 IL/Asm 分析。高階語言、架構設計、演算法才是解決實際問題的主戰場。