如何通过查看程序集来判断程序是否使用动态分派

时间:2012-04-03 14:56:24

标签: oop x86 dynamic-dispatch

我在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]

我认为这是动态调度吗?如果是的话,还有其他任何迹象吗?如果我错了,我怎么能判断它是否是动态调度?

2 个答案:

答案 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查找)调用。当然更快。