C#: вызов Виртуальной функции еще быстрее, чем вызов делегата?

Вы должны сделать это как динамический SQL.

Declare @dbname nvarchar(50)
set @dbname = 'MyDatabase_Test'

EXEC('ALTER DATABASE MyDatabase MODIFY NAME = ' + @dbname);
14
задан Jon Skeet 19 October 2008 в 07:32
поделиться

7 ответов

Думайте о том, что требуется в каждом случае:

Виртуальный вызов

  • Проверьте на ничтожность
  • Перейдите от объектного указателя для ввода указателя
  • Ищите адрес метода в таблице инструкции
  • (Не уверенный - даже Richter не покрывает это), Переходят к базовому типу, если метод не переопределяется? Рекурсивно вызовите, пока мы не найдем правильный адрес метода. (Я не думаю, таким образом - посмотрите редактирование в основе.)
  • Продвиньте указатель исходного объекта на стек ("это")
  • Назовите метод

Вызов делегата

  • Проверьте на ничтожность
  • Перейдите от объектного указателя до массива вызовов (все делегаты потенциально многоадресно передаются),
  • Цикл по массиву, и для каждого вызова:
    • Адрес метода выборки
    • Удайтесь, передать ли цель как первый аргумент
    • Продвиньте аргументы на стек (уже, возможно, был сделан - не уверенный),
    • Дополнительно (в зависимости от того, открыт ли вызов или закрывается), продвигают цель вызова на стек
    • Назовите метод

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

Но в основном существует такая же косвенность, связанная с делегатом. Учитывая бит я не уверен в в виртуальном вызове метода, возможно, что вызов к непереопределенному виртуальному методу в в широком масштабе глубокой иерархии типа был бы медленнее... Я дам ему попытку и редактирование с ответом.

Править: Я попытался играть вокруг с обоими глубина иерархии наследования (до 20 уровней), точка "наиболее полученного переопределения" и заявленного типа переменной - и ни один из них, кажется, не имеет значения.

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

20
ответ дан 1 December 2019 в 06:40
поделиться

Просто требуемый для добавления нескольких исправлений к ответу стрельбы по тарелочкам Джона:

Виртуальный вызов метода не должен делать пустой проверки (автоматически обработанный с аппаратными сообщениями).

Это также не должно идти по цепочке наследования для нахождения non-overriden методов (это - то, что таблица виртуальных методов для).

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

Вызов делегата также включает дополнительный уровень абстракции.

Вызовы делегату не вовлекают аргументы помещения в массив, если Вы не выполняете, динамическое вызывают использование метода DynamicInvoke.

Вызов делегата включает вызывающий метод, называя компилятор сгенерированным, Вызывают метод на рассматриваемый тип делегата. В вызов к predicator (значение) превращаются predicator. Вызовите (оценивают).

Вызвать метод в свою очередь реализован JIT для вызова указателя (указателей) функции (сохраненным внутренне в объекте делегата).

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

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

Различие могло произойти из-за потребности к дополнительному checks+branches из-за многоадресной передачи (как предложил John). Другая причина могла состоять в том, что JIT-компилятор не встраивает Делегата. Вызовите метод и реализацию Делегата. Вызовите не обрабатывает аргументы, а также реализацию при выполнении виртуальных вызовов метода.

12
ответ дан 1 December 2019 в 06:40
поделиться

Виртуальный вызов разыменовывает два указателя при известном смещении в памяти. Это не на самом деле динамическое связывание; нет никакого кода во времени выполнения для отражения по метаданным для обнаружения правильного метода. Компилятор генерирует несколько инструкций сделать вызов, на основе этого указателя. на самом деле виртуальный вызов является единственной инструкцией IL.

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

Я предложил бы, чтобы Вы посмотрели на конструкции IL для обоих. Скомпилируйте упрощенную версию своего источника выше с единственным вызовом к каждому из двух DoSomthing. Затем используйте ILDASM для наблюдения то, что является фактическим кодом для каждого шаблона.

(И я уверен, что получу downvoted для того, чтобы не использовать правильную терминологию :-))

8
ответ дан 1 December 2019 в 06:40
поделиться

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

Для чего-то вроде этого обычно лучше проверить его, поскольку Вы сделали, чем попытка предположить, какова производительность будет. Если Вы хотите знать больше о том, как вызов делегата работает, я предлагаю превосходную книгу "CLR Через C#" Jeffrey Richter.

1
ответ дан 1 December 2019 в 06:40
поделиться

Я сомневаюсь, что это составляет все Ваше различие, но одна вещь первое, что пришло на ум, которая может составлять часть различия, состоит в том, что виртуальная отправка метода уже имеет this указатель, готовый пойти. При вызове через делегата this указатель должен быть выбран от делегата.

Обратите внимание, что в соответствии с этой статьей блога различие было еще больше в.NET v1.x.

1
ответ дан 1 December 2019 в 06:40
поделиться

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

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

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

-1
ответ дан 1 December 2019 в 06:40
поделиться

] Результат теста, состоящий из 1000 слов: http://kennethxu.blogspot.com/2009/05/strong-typed-high-performance_15.html

3
ответ дан 1 December 2019 в 06:40
поделиться
Другие вопросы по тегам:

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