Я знаю, что полиморфизм может добавить заметные накладные расходы.Вызов виртуальной функции медленнее, чем вызов не виртуальной.(Весь мой опыт касается GCC, но я думаю / слышал, что это верно для любого реального компилятора.)
Много раз данная виртуальная функция вызывается для одного и того же объекта снова и снова;Я знаю, что тип объекта не меняется, и в большинстве случаев компилятор может легко вывести, что хорошо:
BaseType &obj = ...;
while( looping )
obj.f(); // BaseType::f is virtual
Чтобы ускорить код, я мог бы переписать приведенный выше код следующим образом:
BaseType &obj = ...;
FinalType &fo = dynamic_cast< FinalType& >( obj );
while( looping )
fo.f(); // FinalType::f is not virtual
Интересно, как лучше всего избежать этих издержек из-за полиморфизма в этих случаях.
Идея верхнего приведения (как показано во втором фрагменте) не выглядит мне так хорошо: BaseType
может быть унаследовано многими классами, и попытка привести к верхнему приведению ко всем из них будет довольно распространенной.
Другая идея может состоять в том, чтобы сохранить obj.f
в указателе функции (непроверьте это, не уверен, что это убьет накладные расходы во время выполнения), но опять-таки этот метод не выглядит идеальным: как и в предыдущем методе, он потребует написания большего количества кода и не сможет использовать некоторые оптимизации (например:если бы FinalType::f
была встроенной функцией, она не стала бы встроенной - но я думаю, что единственный способ избежать этого - это преобразовать obj
в окончательный тип ...)
, есть ли лучший метод?
Редактировать: Ну, конечно, это не так сильно повлияет.Этот вопрос главным образом состоял в том, чтобы узнать, было ли что-то делать, так как похоже, что эти накладные расходы предоставляются бесплатно (эти накладные расходы, по-видимому, очень легко убить). Я не понимаю, почему бы и нет.легкое ключевое слово для небольших оптимизаций, таких как C99 restrict
, чтобы сказать компилятору, что полиморфный объект имеет фиксированный тип, - это то, на что я надеялся.,Посмотрите на этот ad-hoc extreme код:
struct Base { virtual void f(){} };
struct Final : public Base { void f(){} };
int main( ) {
Final final;
Final &f = final;
Base &b = f;
for( int i = 0; i < 1024*1024*1024; ++ i )
#ifdef BASE
b.f( );
#else
f.f( );
#endif
return 0;
}
Компиляция и запуск, время:
$ for OPT in {"",-O0,-O1,-O2,-O3,-Os}; do
for DEF in {BASE,FINAL}; do
g++ $OPT -D$DEF -o virt virt.cpp &&
TIME="$DEF $OPT: %U" time ./virt;
done;
done
BASE : 5.19
FINAL : 4.21
BASE -O0: 5.22
FINAL -O0: 4.19
BASE -O1: 3.55
FINAL -O1: 1.53
BASE -O2: 3.61
FINAL -O2: 0.00
BASE -O3: 3.58
FINAL -O3: 0.00
BASE -Os: 6.14
FINAL -Os: 0.00
Я думаю, только -O2, -O3 и-Оы встраивают Final::f
.
И эти тесты были проведены на моей машине с новейшим GCC и двухъядерным процессором AMD Athlon (tm) 64 X2 4000+.Я думаю, это может быть намного медленнее на более дешевой платформе.