我在Herb Stutter: JIT will never be as fast as native读了一篇关于Reddit的帖子,并且有人评论说,有人称Herb“误称”C#使用虚拟方法而不是非虚拟方法(你可以阅读文章{{3} })。它让我思考,我做了一个快速的小程序,并注意到C#确实为CIL生成虚拟方法(callvirt vs call)。但后来我被告知这并不容易,JIT可能会内联代码而不是使用vtable和动态调度。我启动了调试器并尝试查看。这是我的简单程序:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Test t = new Test();
t.TestIt();
t.TestOut();
}
}
class Test
{
public Test() { }
public void TestIt()
{
Console.WriteLine("TESTIT");
}
public void TestOut()
{
Console.WriteLine("TESTOUT");
}
}
}
然后是集会:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Test t = new Test();
00000000 push ebp
00000001 mov ebp,esp
00000003 push esi
00000004 mov ecx,439E68h
00000009 call FFCE0AD4
0000000e mov esi,eax
t.TestIt();
00000010 call 704BBEB8 // Call to Console.WriteLine()
00000015 mov ecx,eax
00000017 mov edx,dword ptr ds:[02A02088h]
0000001d mov eax,dword ptr [ecx]
0000001f call dword ptr [eax+000000D8h]
t.TestOut();
00000025 call 704BBEB8 // Call to Console.WriteLine()
0000002a mov ecx,eax
0000002c mov edx,dword ptr ds:[02A0208Ch]
00000032 mov eax,dword ptr [ecx]
00000034 call dword ptr [eax+000000D8h]
0000003a pop esi
}
0000003b pop ebp
0000003c ret
我的问题是:通过查看程序集如何判断它是否使用动态调度?我的预感是因为这4条指令类似于我在编程语言课程中记得的内容:
0000002a mov ecx,eax
0000002c mov edx,dword ptr ds:[02A0208Ch]
00000032 mov eax,dword ptr [ecx]
00000034 call dword ptr [eax+000000D8h]
我认为这是动态调度吗?如果是的话,还有其他任何迹象吗?如果我错了,我怎么能判断它是否是动态调度?
答案 0 :(得分:3)
间接通话,例如call dword ptr [eax+000000D8h]
表示使用虚拟表
答案 1 :(得分:2)
是的,这个模式查找类似vtable的东西,然后使用检索到的地址执行函数调用
00000032 mov eax,dword ptr [ecx]
00000034 call dword ptr [eax+000000D8h]
是动态调度的标志(也称为动态绑定)。该模式基本上执行以下操作:使用对象的地址推导出对象类型(它实际上只是找到存储在对象中的vtable指针)并找到要调用的函数(知道它在vtable中的索引)。如果您已经知道对象的实际类型,则可以直接调用正确的函数。
例如在C ++中:
class Class {
public:
virtual void Method() {}
};
Class* object = new Object();
object->Method();
delete object;
这里编译器有足够的数据知道object
存储了class Class
类型的对象的地址,因此它只能发出对Class::Method()
的直接(无vtable查找)调用。当然更快。