2015-12-26 5 views
0

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

У меня есть иерархия наследования, состоящая из 3 классов: Форма, Прямоугольник, Четырехугольник. У меня есть один класс под названием BaseQuadrilateral, который не наследует ни от чего, и делает то же самое, что и класс Quadrilateral. В каждом классе есть два метода: surfaceArea() и volume(). Я запускаю отдельный тест для каждого класса и записываю время, которое требуется для работы с 10 000 000 объектов. Я ожидал, что четырехсторонний класс займет немного больше времени. Вместо этого класс Quadrilateral (Inherited from 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; 
} 
+1

Эта линия может быть немного, чтобы сделать с ним: 'bqTimer = часы() - qTimer; '. Кажется, вы хотите, чтобы 'qTimer' был' bqTimer'. –

ответ

0

Если эта линия

bqTimer = clock() - qTimer; 

быть

bqTimer = clock() - bqTimer; 
3

Как я уже писал в комментариях, у вас есть немного проблема с этой линии:

bqTimer = clock() - qTimer; 

. Вам кажется, что qTimer будет bqTimer. Однако это не объясняет разницу настолько большую, насколько вы ее заметили. Для этого вам следует внимательно изучить ваши две тестовые циклы и, в частности, то, как они отличаются.

В первом случае, вы записываете указатель на ваш Quadrilateral в переменной shape, а затем вызов метода Volume() косвенно через указатель:

for (int i = 0; i < ARRAY_SIZE; i++) 
{ 
    shape = &quadrilaterals[i]; 
    answers1[i] = shape->Volume(); 
} 

Во втором случае, вы делаете копию все BaseQuadrilateral объекта, а затем, вызывая метод Volume() к копии:

for (int i = 0; i < ARRAY_SIZE; i++) 
{ 
    baseQuadrilateral = baseQuadrilaterals[i]; 
    answers2[i] = baseQuadrilateral.Volume(); 
} 

Копирование объекта гораздо дороже, чем получение его адреса. В этом конкретном случае, по сути, вычисление адресов может даже быть полностью оптимизировано. Я предлагаю вам избегать посредника, и в обоих случаях вызова метода непосредственно на элементах массива:

answers1[i] = quadrilaterals[i].Volume(); 

или

answers2[i] = baseQuadrilaterals[i].Volume(); 
+0

Спасибо, Джон. Я исправил проблему с таймером. Не знаю, как я сам это пропустил. Как только вы указали, что произошла копия с «baseQuadrilateral», я понял, что я не объявил, что это указатель, который я намеревался сделать, чтобы обе петли были эквивалентны.Запуск теста снова дает около 2 секунд для 'quadrilaterals' и 3-4 секунды для' baseQuadrilaterals'. То же самое с предлагаемыми строками кода. Это гораздо более разумный разрыв, но я до сих пор не понимаю, почему «baseQuadrilateral» медленнее, чем «четырехугольник». Почему это не наоборот? –

+0

После запуска теста несколько раз я иногда получаю 'quadrilaterals' немного быстрее, чем' baseQuadrilaterals', на несколько сотен тысяч тиков, но большую часть времени «quadrilaterals» находится между 1-2 секундами и «baseQuadrilaterals» 'идет между 2-3 секундами. –

+0

@KelbyMadal, ваш тест теперь говорит вам одну вещь: разница в производительности между двумя вашими подходами очень мала - порядка одного микросекунды за звонок в лучшем случае для этого случая. Я могу сказать, почему измеренная производительность сравнивается по-разному, чем вы ожидаете, но я подозреваю, что неэквивалентные реализации 'Volume()' (которые вы не представили). Также может быть любое количество тонких различий в оптимизации или тестировании, которые влияют на результаты. –

Смежные вопросы