[英]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.