Эффективность выполнения виртуальных функций C ++ - PullRequest
4 голосов
/ 09 января 2010

Я пытаюсь получить лучшее представление о производительности виртуальных функций. Вот пример кода:

struct Foo {
    virtual void function1();
    virtual void function2() { function1(); }
};

struct Bar : Foo {
    virtual void function1();
}

Bar b;
Foo &f = b;

b.function2();
b.function1();
f.function2();

для каждого из трех вызовов в последних трех строках примера кода все они должны искать указатель функции в виртуальной таблице? сколько поиска нужно сделать для найденного объекта. который когда-то может быть встроен компилятором?

спасибо

Ответы [ 6 ]

7 голосов
/ 09 января 2010

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

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

Теперь ситуация, когда вы вызываете function2 для f, хитрая по двум причинам: функция никогда не переопределяется, и вы используете ссылку, которую нельзя переназначить. Следовательно, действительно умный компилятор, который видит все входные файлы, мог бы сообразить, что цель вызова на самом деле будет иметь 100% достоверность (так как вы не собираетесь добавлять новые классы в уже скомпилированный код). Тем не менее, AFAIK, компиляторы не должны это делать, чтобы вы заплатили за это.

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

5 голосов
/ 09 января 2010

Элан Раскин сделал тест на эту тему:

http://assemblyrequired.crashworks.org/2009/01/19/how-slow-are-virtual-functions-really/

Конечно, вы сами решаете, насколько практичны его результаты для вас: -)

0 голосов
/ 13 сентября 2013

В наши дни последствия для производительности ничтожно малы. Даже в играх или другом ресурсоемком коде.

0 голосов
/ 09 января 2010

Фактический вызов метода занимает в два раза больше времени для виртуального метода. Имейте в виду, что для вставки ничего не требуется. Если вы в первую очередь согласны с вызовом, вы, как правило, в порядке и с виртуальным вызовом, а когда нет, вы часто можете вставить его. Особенно имейте в виду, что время вызова не учитывает фактический код, который затем запускается.

0 голосов
/ 09 января 2010
In C++, To achieve run-time polymorphism 2 things are required:
1. Function must be a member of the class and declared as virtual.
2. Use Pointers or references to make dynamic function dispatch possible.

e.g.
   Derived d;
   Base &refBase = d;
   Base *pBase = d;

   d.foo();
   // resolved at compile time based on the static type of object (Derived)

   refBase.foo();  //will resolve at runtime
/* Here refBase is reference to Base class , but its dynamic type reference to 
Derived class object so its referring to derived class object and accordingly derived  class function is called (using look up in vtbl) and similarly for pointers.
*/

 So in your case first 2 function calls are resolved at compiler time since invoked by
 object rather than pointers or references .

 The last call will be resolved at run-time by making  lookup in vtable of derived class. 
0 голосов
/ 09 января 2010

Вот что вы можете попробовать. Оберните эти три вызова функций в цикл for с миллионом итераций. Затем запустите программу через профилировщик, чтобы почувствовать разницу в стоимости. Если вы запустите linux, callgrind (профилировщик) и kcachegrind (визуализируйте результаты) даст вам отличное понимание этого, даже до уровня кода ассемблера.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...