性能与所述直接"虚拟呼叫与在C#接口调用 [英] Performance of "direct" virtual call vs. interface call in C#

查看:98
本文介绍了性能与所述直接"虚拟呼叫与在C#接口调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这一基准似乎表明,直接调用虚方法的对象引用比调用它的参考接口这个对象更快。工具

This benchmark appears to show that calling a virtual method directly on object reference is faster than calling it on the reference to the interface this object implements.

在换句话说:

interface IFoo {
    void Bar();
}

class Foo : IFoo {
    public virtual void Bar() {}
}

void Benchmark() {
    Foo f = new Foo();
    IFoo f2 = f;
    f.Bar(); // This is faster.
    f2.Bar();    
}



从C ++世界的到来,我本来期望,这两个电话会进行相同的实施(作为一个简单的虚拟表查找),并有相同的性能。如何C#实现虚拟来电,什么是通过接口调用时,显然被这样做额外的工作?

Coming from the C++ world, I would have expected that both of these calls would be implemented identically (as a simple virtual table lookup) and have the same performance. How does C# implement virtual calls and what is this "extra" work that apparently gets done when calling through an interface?

确定,答案/评论我走到这一步,意味着有一个双指针解除引用通过接口与只有一个解引用,用于通过物体虚拟呼叫虚拟呼叫。

OK, answers/comments I got so far imply that there is a double-pointer-dereference for virtual call through interface versus just one dereference for virtual call through object.

所以,可以请别人解释的为什么是必要的吗?什么是虚拟表在C#中的结构?它是扁平化(这是典型的C ++)或不?是什么在C#语言设计导致这种所做的设计权衡?我并不是说这是一个坏的设计,我,为什么有必要只是好奇。

So could please somebody explain why is that necessary? What is the structure of the virtual table in C#? Is it "flat" (as is typical for C++) or not? What were the design tradeoffs that were made in C# language design that lead to this? I'm not saying this is a "bad" design, I'm simply curious as to why it was necessary.

在概括地说,我想< STRONG>了解引擎盖下我的工具做什么,所以我可以更有效地使用它。如果我没有得到任何更多的你应该不知道或用另一种语言类型的答案,我将不胜感激。

In a nutshell, I'd like to understand what my tool does under the hood so I can use it more effectively. And I would appreciate if I didn't get any more "you shouldn't know that" or "use another language" types of answers.

只是要清楚,我们面对的不是这里的JIT优化,消除动态分配的一些编译器:我修改了原来的问题所提到的基准实例化一个类或在运行时另一只。由于编译后和组件加载/ JIT编译后的实例发生,有没有办法避免这两种情况下的动态调度:

Just to make it clear we are not dealing with some compiler of JIT optimization here that removes the dynamic dispatch: I modified the benchmark mentioned in the original question to instantiate one class or the other randomly at run-time. Since the instantiation happens after compilation and after assembly loading/JITing, there is no way to avoid dynamic dispatch in both cases:

interface IFoo {
    void Bar();
}

class Foo : IFoo {
    public virtual void Bar() {
    }
}

class Foo2 : Foo {
    public override void Bar() {
    }
}

class Program {

    static Foo GetFoo() {
        if ((new Random()).Next(2) % 2 == 0)
            return new Foo();
        return new Foo2();
    }

    static void Main(string[] args) {

        var f = GetFoo();
        IFoo f2 = f;

        Console.WriteLine(f.GetType());

        // JIT warm-up
        f.Bar();
        f2.Bar();

        int N = 10000000;
        Stopwatch sw = new Stopwatch();

        sw.Start();
        for (int i = 0; i < N; i++) {
            f.Bar();
        }
        sw.Stop();
        Console.WriteLine("Direct call: {0:F2}", sw.Elapsed.TotalMilliseconds);

        sw.Reset();
        sw.Start();
        for (int i = 0; i < N; i++) {
            f2.Bar();
        }
        sw.Stop();
        Console.WriteLine("Through interface: {0:F2}", sw.Elapsed.TotalMilliseconds);

        // Results:
        // Direct call: 24.19
        // Through interface: 40.18

    }

}



编辑--- 3 ---



如果有人有兴趣,这里是我的Visual C ++ 2010如何勾画出一个类的实例乘法继承其他类:

--- EDIT 3 ---

If anyone is interested, here is how my Visual C++ 2010 lays out an instance of a class that multiply-inherits other classes:

代码:

class IA {
public:
    virtual void a() = 0;
};

class IB {
public:
    virtual void b() = 0;
};

class C : public IA, public IB {
public:
    virtual void a() override {
        std::cout << "a" << std::endl;
    }
    virtual void b() override {
        std::cout << "b" << std::endl;
    }
};



调试器:

Debugger:

c   {...}   C
    IA  {...}   IA
        __vfptr 0x00157754 const C::`vftable'{for `IA'} *
            [0] 0x00151163 C::a(void)   *
    IB  {...}   IB
        __vfptr 0x00157748 const C::`vftable'{for `IB'} *
            [0] 0x0015121c C::b(void)   *

多个虚拟表指针清晰可见,而的sizeof(C)== 8 (32位版本)。

Multiple virtual table pointers are clearly visible, and sizeof(C) == 8 (in 32-bit build).

...

C c;
std::cout << static_cast<IA*>(&c) << std::endl;
std::cout << static_cast<IB*>(&c) << std::endl;

.. ...打印

0027F778
0027F77C

...这表明指针对同一个对象的不同接口,实际却指向该对象的不同部分(即它们包含不同的物理地址)。

...indicating that pointers to different interfaces within the same object actually point to different parts of that object (i.e. they contain different physical addresses).

推荐答案

我在思考 http://msdn.microsoft.com/en-us/magazine/cc163791.aspx <文章/ A>将会回答你的问题。特别是,请参见接口V表地图和地图接口和下面的部分上虚拟调度。

I think the article at http://msdn.microsoft.com/en-us/magazine/cc163791.aspx will answer your questions. In particular, see the section Interface Vtable Map and Interface Map, and the following section on Virtual Dispatch.

这也许是可能的JIT编译器理出头绪,优化代码的简单情况。但不是在一般情况下

It's probably possible for the JIT compiler to figure things out and optimize the code for your simple case. But not in the general case.

IFoo f2 = GetAFoo();

GetAFoo 被定义为返回一个的IFoo ,那么JIT编译器就无法优化呼叫。

And GetAFoo is defined as returning an IFoo, then the JIT compiler wouldn't be able to optimize the call.

这篇关于性能与所述直接&QUOT;虚拟呼叫与在C#接口调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆