Производительность «прямой» виртуальный вызов VS. Интерфейсный вызов в C #

Этот показатель , по-видимому, показывает, что вызов виртуального метода непосредственно на ссылке объекта является быстрее, чем вызывая его на ссылку на интерфейс этого объекта.

Другими словами:

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 # реализует виртуальные вызовы и в чем эта «дополнительная» работа, которая, по-видимому, сделана при вызове через интерфейс?

--- Редактировать ---

ОК, ответы / комментарии, которые я получил до сих пор подразумевает, что есть Двойной указатель-неработающий для виртуального вызова через интерфейс против всего лишь одного размышления для виртуального вызова через объект.

Так что мог угодно объяснить Почему это необходимо? Какова структура виртуальной таблицы в C #? Это «плоский» (как типично для C ++) или нет? Каковы были проектные компромиссы, которые были сделаны в языковой конструкции C #, которые приводят к этому? Я не говорю, что это «плохой» дизайн, я просто любопытно, почему это было необходимо.

в двух словах, я бы хотел понять , что мой инструмент делает под капотом, поэтому я могу использовать его более эффективно. И я был бы признателен, если бы у меня больше не получишь ", вы не должны знать, что« или «использовать другой язык» типов ответов.

--- Редактировать 2 ---

Просто чтобы понять, что мы не имеем дело с некоторыми компиляторами jiT Optimization здесь, что удаляет динамическую динамику: я изменил тест, упомянутый в оригинальном вопросе, чтобы создать один класс или другой случайно во время выполнения. Поскольку мнение происходит после компиляции, а после сборки loading / jity, нет способа избежать динамической рассылки в обоих случаях:

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 год устанавливает экземпляр класса, который размножается - наследует другие классы:

код:

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;
    }
};

Отладчик:

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-битной сборке).

.. ...

C c;
std::cout << static_cast(&c) << std::endl;
std::cout << static_cast(&c) << std::endl;

.. Принты ...

0027F778
0027F77C

... Указание того, что указатели на разные интерфейсы в одном и том же объекте фактически указывают на разные части этого объекта (то есть они содержат разные физические адреса).

60
задан Branko Dimitrijevic 1 October 2011 в 13:15
поделиться