[英]Performance of C# method polymorphism with generics
我注意到在C#中,與C ++不同,您可以組合虛擬和通用方法。 例如:
using System.Diagnostics;
class Base {
public virtual void Concrete() {Debug.WriteLine("base concrete");}
public virtual void Generic<T>() {Debug.WriteLine("base generic");}
}
class Derived : Base {
public override void Concrete() {Debug.WriteLine("derived concrete");}
public override void Generic<T>() {Debug.WriteLine("derived generic");}
}
class App {
static void Main() {
Base x = new Derived();
x.Concrete();
x.Generic<PerformanceCounter>();
}
}
鑒於可以實例化任何數量的Generic<T>
版本,看起來不像標准的vtbl
方法可用於解析方法調用,事實上它並非如此。 這是生成的代碼:
x.Concrete();
mov ecx,dword ptr [ebp-8]
mov eax,dword ptr [ecx]
call dword ptr [eax+38h]
x.Generic<PerformanceCounter>();
push 989A38h
mov ecx,dword ptr [ebp-8]
mov edx,989914h
call 76A874F1
mov dword ptr [ebp-4],eax
mov ecx,dword ptr [ebp-8]
call dword ptr [ebp-4]
額外的代碼似乎是根據通用參數查找動態vtbl,然后調用它。 有沒有人寫過這個實現的具體細節? 與非通用案例相比,它的表現如何?
.NET泛型實現可以輕松地處理這樣的場景並且具有非常好的性能。 我剛剛寫了一篇關於它的博客文章 。
Micosoft Research撰寫的這篇論文是查找有關CLR如何實現泛型的信息的最佳資源之一。
你有關於vtable的事情。 當JIT編譯器偶然發生時,CLR如何為泛型類型創建可執行代碼取決於泛型類型參數。 值類型和引用類型的處理方式不同。
雖然可執行代碼(實例化,本機映像)在作為引用類型的所有泛型類型參數的實例化之間共享,但與實例化的實例(對象)相關聯的vtable對於具體參數類型是唯一的。
以下是上述論文的相關引用:
4.2對象表示CLR的垃圾收集堆中的對象由vtable指針表示,后跟對象的內容(例如字段或數組元素)。 vtable的主要作用是虛方法調度:它包含由對象類定義或繼承的每個方法的代碼指針。 但是對於簡單的類類型,至少在vtable和類之間存在一對一的對應關系時,它也可以用來表示對象的類型。 當以這種方式使用vtable時,我們將其稱為類型的類型句柄。 在基於完全特化的多態的實現中,精確運行時類型的概念是免費的,因為相同參數化類型的不同實例具有不同的vtable。 但現在假設代碼在不同的實例之間共享,例如
List<string>
和List<object>
。 兩個實例的vtable將是相同的,因此我們需要某種方式來表示運行時的實例化。
...
[對於每個實例化,我們]用指向組合的vtable和實例化結構的指針替換vtable指針,並在每個實例化時復制它[結構]。
實現.NET泛型的方式,對於泛型類(或方法)的每次使用,CLR都會創建該類(或方法)的新實現,並填充泛型參數。對於引用類型,它們共享一個實現(因為它們都只是相同大小的指針); 每個結構都有自己的實現,因為大小都不同。
所以我猜測泛型類型/方法的每個實現都有它自己的vtable,並且該代碼正在執行'查找通用實現',然后在它找到的實現上執行'lookup vtable override'。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.