Если вам нужна скорость, рассмотрите возможность встраивания «типа (-идентифицирующего) числа» в объекты и использования оператора switch для выбора кода, специфичного для типа.Это может полностью избежать накладных расходов на вызовы функций - просто выполнить локальный переход.Вы не получите быстрее, чем это.Стоимость (с точки зрения удобства сопровождения, перекомпиляции и т. Д.) Заключается в принудительной локализации (в коммутаторе) функциональности, специфичной для типа.
РЕАЛИЗАЦИЯ
#include <iostream>
#include <vector>
// virtual dispatch model...
struct Base
{
virtual int f() const { return 1; }
};
struct Derived : Base
{
virtual int f() const { return 2; }
};
// alternative: member variable encodes runtime type...
struct Type
{
Type(int type) : type_(type) { }
int type_;
};
struct A : Type
{
A() : Type(1) { }
int f() const { return 1; }
};
struct B : Type
{
B() : Type(2) { }
int f() const { return 2; }
};
struct Timer
{
Timer() { clock_gettime(CLOCK_MONOTONIC, &from); }
struct timespec from;
double elapsed() const
{
struct timespec to;
clock_gettime(CLOCK_MONOTONIC, &to);
return to.tv_sec - from.tv_sec + 1E-9 * (to.tv_nsec - from.tv_nsec);
}
};
int main(int argc)
{
for (int j = 0; j < 3; ++j)
{
typedef std::vector<Base*> V;
V v;
for (int i = 0; i < 1000; ++i)
v.push_back(i % 2 ? new Base : (Base*)new Derived);
int total = 0;
Timer tv;
for (int i = 0; i < 100000; ++i)
for (V::const_iterator i = v.begin(); i != v.end(); ++i)
total += (*i)->f();
double tve = tv.elapsed();
std::cout << "virtual dispatch: " << total << ' ' << tve << '\n';
// ----------------------------
typedef std::vector<Type*> W;
W w;
for (int i = 0; i < 1000; ++i)
w.push_back(i % 2 ? (Type*)new A : (Type*)new B);
total = 0;
Timer tw;
for (int i = 0; i < 100000; ++i)
for (W::const_iterator i = w.begin(); i != w.end(); ++i)
{
if ((*i)->type_ == 1)
total += ((A*)(*i))->f();
else
total += ((B*)(*i))->f();
}
double twe = tw.elapsed();
std::cout << "switched: " << total << ' ' << twe << '\n';
// ----------------------------
total = 0;
Timer tw2;
for (int i = 0; i < 100000; ++i)
for (W::const_iterator i = w.begin(); i != w.end(); ++i)
total += (*i)->type_;
double tw2e = tw2.elapsed();
std::cout << "overheads: " << total << ' ' << tw2e << '\n';
}
}
РЕЗУЛЬТАТЫ ПРОИЗВОДИТЕЛЬНОСТИ
В моей системе Linux:
~/dev g++ -O2 -o vdt vdt.cc -lrt
~/dev ./vdt
virtual dispatch: 150000000 1.28025
switched: 150000000 0.344314
overhead: 150000000 0.229018
virtual dispatch: 150000000 1.285
switched: 150000000 0.345367
overhead: 150000000 0.231051
virtual dispatch: 150000000 1.28969
switched: 150000000 0.345876
overhead: 150000000 0.230726
Это предполагает, что подход с коммутацией номеров встроенных типов равен (1,28 - 0,23).) / (0,344 - 0,23) = 9,2 раз быстрее.Конечно, это зависит от конкретной протестированной системы / флагов и версии компилятора и т. Д., Но в целом показательно.
КОММЕНТАРИИ RE ВИРТУАЛЬНАЯ ОТПРАВКА
Должно бытьТем не менее, это говорит о том, что накладные расходы на вызовы виртуальных функций редко бывают значительными, и то только для часто называемых тривиальных функций (таких как методы получения и установки).Даже в этом случае вы можете предоставить единственную функцию, позволяющую получать и настраивать множество вещей одновременно, сводя к минимуму затраты.Люди слишком сильно беспокоятся о виртуальной рассылке - так что делайте профилирование, прежде чем искать неловкие альтернативы.Основная проблема с ними заключается в том, что они выполняют вызов функции вне линии, хотя они также делокализуют исполняемый код, который изменяет шаблоны использования кэша (к лучшему или (чаще)).