Pages

Monday, August 31, 2009

Как убрать виртуальную функцию, если нужно ускорить программу

Вот простая програмка, демонстрирующая использование полиморфизма:
#include <iostream>
using namespace std;
class A
{
public:
virtual void out() = 0;
};
class B : public A
{
int x;
public:
B() : x (12345) {}
void out()
{
cout << "object of class B. x = " << x << endl;
}
};

class C : public A
{
double x;
public:
C() : x (123.456789) {}
void out()
{
cout << "object of class C. x = " << x << endl;
}
};
int main()
{
A* p = new B;
p->out();
delete p;
p = new C;
p->out();
delete p;
return 0;
}

В таком простом случае использование виртульных функций оправдано. А что, если такую виртуальную функцию нужно было бы вызвать в цикле миллион раз?
for (int i=0; i < 1000000; i++) 
p->out();
Тело цикла имеет только одну строку - вызывается только одна функция. И если нужно такой код оптимизировать то что же тут делать?
Да хотя бы не тратить время на игру с полиморфизмом. Если знать, что в данный момент идет работа с объектом класса А, то и использовать класс А:
for (int i=0; i < 1000000; i++) 
reinterpret_cast<const T*>(p)->out();
Ну не так, конечно. Это, просто шутка. Вот так:
A* pA = reinterpret_cast<const T*>(p);
for (int i=0; i < 1000000; i++)
pA->out();
И в этом примере функцию out() нужно искать в таблице виртуальных методов.
Лучше всего, если уж говорить об ускорении программы, не использовать виртуальность. Например, классы из первой программы будут выглядеть так:
template <typename T>
class A
{
public:
void out()
{
reinterpret_cast<const T*>(this)->out();
}
};

class B : public A<B>
{
int x;
public:
B() : x (12345) {}

void out()
{
cout << "object of class B. x = " << x << endl;
}
};

class C : public A<C>
{
double x;
public:
C() : x (123.456789) {}
void out()
{
cout << "object of class C. x = " << x << endl;
}
};

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

No comments:

Post a Comment