Скорость виртуального вызова в C# по сравнению с C++

Я не учел, что переход на летнее время начинается с даты в примере. Спасибо sellotape за указание на это.

8
задан Johann Gerell 24 March 2009 в 10:59
поделиться

9 ответов

Виртуальный вызов C# должен проверить на “этот” являющийся пустым, и виртуальный вызов C++ не делает. Таким образом, я не вижу в обычно, почему виртуальные вызовы C# были бы быстрее. В особых случаях компилятор C# (или JIT-компилятор) может встраивать виртуальный вызов лучше затем компилятор C++, поскольку компилятор C# имеет доступ для лучше ввода информации. Инструкция по методу вызова может иногда быть медленнее в C++, поскольку JIT C# может использовать более быструю инструкцию, которая только справляется с маленьким смещением, поскольку это знает больше о расположении памяти во время выполнения и модели процессора затем компилятор C++.

Однако мы говорим о горстке инструкции по процессору самое большее здесь. На модемном суперскалярном процессоре очень возможно, что “пустая проверка” сообщает, выполняется в то же время, что и “метод вызова” и поэтому занимает время.

Также вероятно, что все инструкции по процессору уже будут в быть кэшем уровня 1, если вызов будет, делают в цикле. Но данные, менее вероятно, будут кэшами, стоимость чтения значения данных от оперативной памяти в эти дни совпадает с выполнением 100 с инструкций от кэша уровня 1. Поэтому это неудачно, что в реальных приложениях стоимость виртуального вызова даже измерима в более затем очень немногих местах.

То, что код C# использует еще несколько инструкций, конечно, уменьшит объем кода, который может поместиться в кэш, эффект этого невозможно предсказать.

(Если класс C++ использует несколько неотъемлемость затем, стоимость больше, из-за необходимости исправить “этот” указатель. Аналогично интерфейсы в C# добавляют другой уровень перенаправления.)

8
ответ дан 5 December 2019 в 07:13
поделиться

Поскольку JIT скомпилировал языки (я не знаю, делает ли CLR это или нет, JVM Sun делает), это - общая оптимизация для преобразования виртуального вызова, который имеет только две или три реализации в последовательность тестов на типе и прямых или встроенных вызовах.

Преимущество этого состоит в том, что современные конвейерные центральные процессоры могут использовать предсказание ветвлений и упреждающую выборку прямых вызовов, но непрямой вызов (представленный указателем функции на высокоуровневых языках) часто приводит к конвейерной остановке.

В ограничивающем случае, где существует только одна реализация виртуального вызова и тело вызова, является достаточно маленьким, виртуальный вызов уменьшил до чисто встроенного кода. Эта техника использовалась в Сам время выполнения языка, который JVM, развитая из.

Большинство компиляторов C++ не выполняет целый анализ программы, требуемый выполнить эту оптимизацию, но проекты, такие как LLVM смотрят на целые оптимизации программы, такие как это.

5
ответ дан 5 December 2019 в 07:13
поделиться

Исходный вопрос говорит:

Я, кажется, вспоминаю чтение где-нибудь, что стоимость виртуального вызова в C# не так высока, собственно говоря, как в C++.

Отметьте акцент. Другими словами, вопрос мог бы быть перефразирован как:

Я, кажется, вспоминаю чтение где-нибудь, что в C#, виртуальные и невиртуальные вызовы являются одинаково медленными, тогда как в C++ виртуальный вызов медленнее, чем невиртуальный вызов...

Таким образом, корреспондент не утверждает, что C# быстрее, чем C++ при любых обстоятельствах.

Возможно бесполезная диверсия, но это зажгло мое любопытство относительно C++ с/clr:pure, не используя C++ / расширения CLI. Компилятор производит IL, который преобразовывается в собственный код JIT, хотя это - чистый C++. Таким образом, здесь у нас есть способ видеть то, что стандартная реализация C++ делает при работе той же платформы как C#.

С невиртуальным методом:

struct Plain
{
    void Bar() { System::Console::WriteLine("hi"); }
};

Этот код:

Plain *p = new Plain();
p->Bar();

... причины call код операции, который будет испускаться с определенным именем метода, передающая Панель неявное this аргумент.

call void <Module>::Plain.Bar(valuetype Plain*)

Сравните с иерархией наследования:

struct Base
{
    virtual void Bar() = 0;
};

struct Derived : Base
{
    void Bar() { System::Console::WriteLine("hi"); }
};

Теперь, если мы делаем:

Base *b = new Derived();
b->Bar();

Это испускает calli код операции вместо этого, который переходит к вычисленному адресу - таким образом, существует много IL перед вызовом. Путем возвращения его в к C# мы видим то, что продолжается:

**(*((int*) b))(b);

Другими словами, бросьте адрес b к указателю на интервал (который, оказывается, тот же размер как указатель) и принимают значение в том местоположении, которое является адресом vtable, и затем возьмите первый объект в vtable, которое является адресом, чтобы перейти к, разыменовать его и назвать его, передавая его неявное this аргумент.

Мы можем настроить виртуальный пример для использования C++ / расширения CLI:

ref struct Base
{
    virtual void Bar() = 0;
};

ref struct Derived : Base
{
    virtual void Bar() override { System::Console::WriteLine("hi"); }
};

Base ^b = gcnew Derived();
b->Bar();

Это генерирует callvirt код операции, точно как это было бы в C#:

callvirt instance void Base::Bar()

Таким образом, при компиляции для предназначения для CLR текущий компилятор C++ Microsoft не имеет тех же возможностей для оптимизации, как C# делает при использовании стандартных функций каждого языка; для стандартной иерархии класса C++ компилятор C++ генерирует код, который содержит трудно кодированную логику для того, чтобы пересечь vtable, тогда как для касательно класса это предоставляет JIT право выяснять оптимальную реализацию.

4
ответ дан 5 December 2019 в 07:13
поделиться

Я предполагаю, что это предположение основано на JIT-компиляторе, означая, что C#, вероятно, преобразовывает виртуальный вызов в вызов простого метода немного, прежде чем это будет на самом деле использоваться.

Но это чрезвычайно теоретически, и я не держал бы пари на нем!

3
ответ дан 5 December 2019 в 07:13
поделиться

Не уверенный в полной платформе, но в Компактной Платформе это будет более медленная причина, CF не имеет никаких таблиц виртуального вызова, хотя она действительно кэширует результат. Это означает, что виртуальный вызов в CF будет медленнее в первый раз, когда это называют, поскольку он должен сделать ручной поиск. Это может быть медленно каждый раз, когда это называют, если приложение является низким на памяти, поскольку кэшируемый поиск может быть передан.

1
ответ дан 5 December 2019 в 07:13
поделиться

Стоимость виртуального вызова в C++ является стоимостью вызова функции через указатель (vtbl). Я сомневаюсь, что C# может сделать тот один быстрее и все еще способность определить тип объекта во времени выполнения...

Править: Как Pete Kirkham указал, хороший JIT смог встраивать вызов C#, избежав останова конвейерной обработки; что-то большинство компиляторов C++ (еще) не может сделать. С другой стороны, Ian Ringrose упомянул влияние на использование кэша. При добавлении к этому самого JIT, работающего, и (строго лично), я не обеспокоился бы действительно, если профилирование на целевой машине под реалистическими рабочими нагрузками не доказало ту, чтобы быть быстрее, чем другой. Это - микрооптимизация в лучшем случае

2
ответ дан 5 December 2019 в 07:13
поделиться

В C# могло бы быть возможно преобразовать виртуальную функцию в невиртуальный путем анализа кода. На практике это, будет оказываться, достаточно часто не будет иметь много значения.

0
ответ дан 5 December 2019 в 07:13
поделиться

C# сглаживает vtable и встраивает вызовы предка, таким образом, Вы не объединяете в цепочку иерархию наследования для разрешения чего-либо.

0
ответ дан 5 December 2019 в 07:13
поделиться

Это может быть не точно ответ на Ваш вопрос, но хотя.NET, JIT оптимизирует виртуальные вызовы, как все сказали прежде, ведомая профилем оптимизация в Visual Studio, 2005 и 2008 делают предположение виртуального вызова путем вставки прямого вызова наиболее вероятной целенаправленной функции, встраивания вызова, таким образом, вес может быть тем же.

0
ответ дан 5 December 2019 в 07:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: