C ++:BIG类的性能影响(包含大量代码)

时间:2009-09-15 08:35:55

标签: c++ performance

我想知道是否以及如何在c ++中编写“全能”类实际上会影响性能。

如果我有一个类,只有 uint x; uint y; 作为数据,几乎已经定义了数学可以作为方法的一切。其中一些方法可能很庞大。 (copy-)构造函数只是初始化两个数据成员。

class Point
{
   int mx; int my;
   Point(int x, int y):mx(x),my(y){};
   Point(const Point& other):mx(other.x),my(other.y){};
 // .... HUGE number of methods....
};

现在。我加载一个大图像并为每个像素创建一个 Point ,将em填充到一个向量中,然后使用它们。 (比方说,所有方法都被调用一次) 这只是一个愚蠢的例子!

如果没有这些方法但是有很多实用功能,它会比同一个类慢吗?我不是以任何方式讨论虚函数!

我对此的动机:我经常发现自己编写了很好且功能相对较强的课程,但是当我必须像上面的例子那样初始化/使用大量的课程时,我会感到紧张。 我想我不应该。

我认为我知道的是:

  1. 方法在内存中只存在一次。 (优化除外)
  2. 分配 仅发生数据 成员,他们是唯一的 复制。
  3. 所以没关系。我错过了什么吗?

9 个答案:

答案 0 :(得分:10)

你是对的,方法只在内存中存在一次,它们就像普通函数一样,额外隐藏了这个参数。

当然,只考虑数据成员的分配,好吧,继承可能会为对象大小的vptrs引入一些额外的ptrs,但不是什么大不了的事

答案 1 :(得分:6)

您已经获得了一些非常好的技术建议。我想投入一些非技术性的东西:由于STL向我们展示了所有这些,在成员函数中完成所有操作可能不是最好的方法。我没有提出争论,而是提到Scott Meyers关于这个主题的课文:How Non-Member Functions Improve Encapsulation

虽然从技术上讲应该没有问题,但您仍然可能希望从设计POV中检查您的设计。

答案 2 :(得分:3)

我认为这比你正在寻找的更多答案,但是这里......

SO充满了人们担心X,Y或Z表现的问题,这种担心是猜测的一种形式。

如果您担心某些事情的表现,请不要担心,找出

以下是该做什么:

  1. 编写程序

  2. Performance tune it

  3. 从经验中学习

  4. 这教会了我,我一遍又一遍地看到它,是这样的:

    • 最佳做法是不要过早优化

    • 最佳实践说 Do 使用大量数据结构类,具有多层抽象,以及最佳的大O算法,“信息隐藏”,事件驱动和通知 - 风格建筑。

    • 性能调整揭示了时间的流逝,其中包括:舞蹈般的普遍性,将山脉从鼹鼠身上移开,调用功能和功能。属性没有实现它们需要多长时间,并且使用指数时间在多个层上执行此操作。

    • 然后问的问题是:大O算法,事件和通知驱动架构等最佳实践背后的原因是什么。答案是:嗯,除其他外,性能

    所以在某种程度上,最佳做法告诉我们:过早优化。明白这点?它说“不要担心性能”,它说“担心性能”,它导致我们尝试不成功而不用担心。我们越担心它,反对我们更好的判断,就越糟糕。

    我的建设性建议如下:按照上面的步骤1,2和3进行操作。这将教会您如何使用最佳实践适度,这将为您提供最佳的全方位设计。

答案 3 :(得分:1)

如果您真的很担心,可以告诉编译器内联构造函数。此优化步骤应该为您提供干净的代码和干净的执行。

答案 4 :(得分:1)

这2位代码是相同的:

Point x;
int l=x.getLength();

int l=GetLength(x);

假设类Point具有非虚方法getLength()。第一次调用实际上调用了int getLength(Point &this),这是我们在第二个例子中写的一个相同的签名。 (*)

如果您调用的方法是虚拟的,这当然不适用,因为所有内容都会通过额外的间接级别(类似于C风格的int l=x->lpvtbl->getLength(x)),更不用说代替对于你实际拥有的每个像素,有2个int,另外一个是指向虚拟表的指针。

(*)这不完全正确,“this”指针通过其中一个cpu寄存器而不是通过堆栈传递,但该机制可以轻松地以任何方式工作。

答案 5 :(得分:1)

第一:不要过早优化。 第二:干净的代码比优化的代码更容易维护。

类的方法有隐藏的this指针,但你不应该担心它。大多数情况下,编译器会尝试通过寄存器传递它。

继承和虚函数在适当的调用中引入了间接(继承=构造函数/析构函数调用,虚函数 - 此函数的每个函数调用)。

短:

  • 您不创建/销毁的对象通常可以使用虚拟方法,继承等,只要它有利于设计。
  • 您创建/销毁的对象通常应该很小(很少有数据成员),并且不应该有很多虚拟方法(最好是完全没有 - 性能明智)。
  • 尝试内联小方法/构造函数。这将减少开销。
  • 如果没有达到预期的性能,请选择干净的设计和重构。

对于具有大型或小型接口的类有不同的讨论(例如,在Scott Meyers(更多)有效的C ++书籍中 - 他选择了最小的接口)。但这与表现无关。

答案 6 :(得分:0)

我创建了与你相同的点类,除了它是一个模板类,所有函数都是内联的。我希望看到性能提升不会降低。但是,尺寸为800x600的图片将具有480k pixels,并且其内存打印将接近4M,而不会显示任何颜色信息。不仅内存而且初始化480k对象将花费太多时间。因此,在这种情况下,我认为这不是一个好主意。但是,如果您使用此类来转换图像的位置,或将其用于图形基元(线条,曲线,圆圈等)

答案 7 :(得分:0)

我同意上述评论:性能和课程布局,并希望添加一条尚未说明设计的评论。

对我来说,就像你过度使用你的Point类一样超出它的真实设计范围。当然,可以以这种方式使用,但应该吗?

在过去的电脑游戏工作中,我经常遇到类似的情况,通常最好的结果是在进行专门的处理(例如图像处理)时,有一个专门的代码集,用于不同的工作out缓冲区效率更高。

这也使您能够以更干净的方式对重要的情况进行性能优化,而不会使基本代码的可维护性降低。

从理论上讲,我确信使用模板代码,具体类设计等复杂组合,并获得几乎相同的运行时效率是一种狡猾的方式...但我通常不愿意进行实施复杂性交易。

答案 8 :(得分:0)

不会将成员函数与对象一起复制。只有数据字段才会影响对象的大小。