Как я понял, вызовы виртуальных функций в классе C++ в иерархии наследования должны быть немного медленнее, чем эквивалентный класс, который не наследуется ни от какого базового класса, то есть автономный класс. Я решил написать небольшую тестовую программу, чтобы увидеть разницу в производительности.
У меня есть иерархия наследования, состоящая из 3 классов: Форма, Прямоугольник, Четырехугольник. У меня есть один класс BaseQuadrilateral, который ничего не наследует и делает то же самое, что и класс Quadrilateral. В каждом классе есть два метода: surfaceArea() и volume(). Я запускаю отдельный тест для каждого класса и записываю время, необходимое для работы с 10 000 000 объектов. Я ожидал, что класс Четырехугольника займет немного больше времени. Вместо этого класс Quadrilateral (унаследованный от Rectangle) работает на порядок быстрее, чем BaseQuadrilateral. Я не понимаю, почему это так.
Test Results:
Running Dynamic Dispatch Test
Quadrilateral Runtime: 2840264 Ticks, 2 Seconds.
BaseQuadrilateral Runtime: 21179219 Ticks, 21 Seconds.
Может ли кто-нибудь объяснить мне, что происходит за кулисами, что делает унаследованный код намного быстрее, и при каких обстоятельствах унаследованный код будет работать медленнее, чем неунаследованный код.
Спасибо
class Shape
{
public:
virtual double surfaceArea() = 0;
virtual double Volume() = 0;
};
class Rectangle : public Shape
{
public:
//Constructors
Rectangle();
Rectangle(double, double);
Rectangle(const Rectangle&);
Rectangle& operator=(const Rectangle&);
double Area();
//Override Shape base class methods
double surfaceArea();
double Volume();
protected:
double length;
double width;
};
class Quadrilateral : public Rectangle
{
public:
//Constructors
Quadrilateral();
Quadrilateral(double, double, double);
Quadrilateral(const Quadrilateral&);
Quadrilateral& operator=(const Quadrilateral&);
//Overloaded Square base class
double surfaceArea();
double Volume();
protected:
double height;
};
class BaseQuadrilateral
{
public:
//Constructors
BaseQuadrilateral();
BaseQuadrilateral(double, double, double);
BaseQuadrilateral(const BaseQuadrilateral&);
BaseQuadrilateral& operator=(const BaseQuadrilateral&);
double surfaceArea();
double Volume();
protected:
double length;
double width;
double height;
};
void test2()
{
clock_t qTimer, bqTimer;
Quadrilateral* quadrilaterals;
BaseQuadrilateral* baseQuadrilaterals, baseQuadrilateral;
Shape* shape;
double* answers1, *answers2;
srand((unsigned int)time(NULL));
cout << "Running Dynamic Dispatch Test\n" << endl;
quadrilaterals = new Quadrilateral[ARRAY_SIZE];
baseQuadrilaterals = new BaseQuadrilateral[ARRAY_SIZE];
answers1 = new double[ARRAY_SIZE];
answers2 = new double[ARRAY_SIZE];
//Initialization
for (int i = 0; i < ARRAY_SIZE; i++)
{
double length = (double)(rand() % 100);
double width = (double)(rand() % 100);
double height = (double)(rand() % 100);
quadrilaterals[i] = Quadrilateral(length, width, height);
baseQuadrilaterals[i] = BaseQuadrilateral(length, width, height);
}
//Test Shape
qTimer = clock();
for (int i = 0; i < ARRAY_SIZE; i++)
{
shape = &quadrilaterals[i];
answers1[i] = shape->Volume();
}
qTimer = clock() - qTimer;
//Test BaseQuadrilateral
bqTimer = clock();
for (int i = 0; i < ARRAY_SIZE; i++)
{
baseQuadrilateral = baseQuadrilaterals[i];
answers2[i] = baseQuadrilateral.Volume();
}
bqTimer = clock() - qTimer;
for (int i = 0; i < ARRAY_SIZE; i++)
{
if (answers1[i] != answers2[i])
{
cout << "Incorrect answer found at i=" << i << ". answers1: " << answers1[i] << " answers2: " << answers2[i] << endl;
break;
}
}
//Print Results
cout << "Quadrilateral Runtime: " << qTimer << " Ticks, " << qTimer / CLOCKS_PER_SEC << " Seconds." << endl;
cout << "BaseQuadrilateral Runtime: " << bqTimer << " Ticks, " << bqTimer / CLOCKS_PER_SEC << " Seconds." << endl;
}
baseQuadrilateral
, я понял, что не объявлял это указателем, что я намеревался сделать, чтобы оба цикла были эквивалентны. Повторный запуск теста дает около 2 секунд дляquadrilaterals
и 3-4 секунды дляbaseQuadrilaterals
. То же самое и с предложенными вами строками кода. Это гораздо более разумный разрыв, но я все еще не понимаю, почемуbaseQuadrilateral
медленнее, чемquadrilateral
. Почему не наоборот? 27.12.2015quadrilaterals
, который немного быстрее, чемbaseQuadrilaterals
, на несколько сотен тысяч тиков, но большую часть времениquadrilaterals
происходит между 1-2 секундами, аbaseQuadrilaterals
- между 2-3 секундами. . 27.12.2015Volume()
(которые вы не представили). Также может быть множество тонких различий в оптимизации или реализации теста, которые влияют на результаты. 28.12.2015Volume()
одинаковы для обоих классов, за исключением, конечно, той, которая помечена как виртуальная в базовом классеShape
.double Quadrilateral::Volume() { return length * width * height; }
double BaseQuadrilateral::Volume() { return length * width * height; }
28.12.2015