C++具有继承类的奇怪性能

C++ strange performance with inherited class

本文关键字:性能 继承 C++      更新时间:2023-10-16

据我所知,继承层次结构中C++类中的虚拟函数调用应该比不从任何基类继承的等价类(即自包含类)慢一点。我决定写一个小的测试程序,看看性能上有什么不同。

我有一个由3个类组成的继承层次结构:形状,矩形,四边形。我有一个名为BaseQuadrilateral的类,它不继承任何东西,并且做与Quadrilateal类相同的事情。每个类中有两个方法:surfaceArea()和volume()。我在每个类上运行一个单独的基准测试,并记录运行10000000个对象所需的时间。我预计四边形课程会花更长的时间。相反,四边形类(从矩形继承)的运行速度和数量级都比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 Testn" << 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;
}

正如我在评论中所写的,您对这一行有点问题:

bqTimer = clock() - qTimer;

您似乎希望qTimerbqTimer。然而,这并不能解释你所观察到的巨大差异。为此,您应该仔细查看您的两个测试循环,特别是它们之间的差异。

在第一种情况下,将指向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();

该行是否应

bqTimer = clock() - qTimer;

bqTimer = clock() - bqTimer;