【问题标题】:C++: Performance impact of BIG classes (with a lot of code)C++:BIG 类的性能影响(包含大量代码)
【发布时间】:2010-11-28 09:28:43
【问题描述】:

我想知道用 C++ 编写“全能”类是否以及如何影响性能。

如果我有一个类 Point,只有 uint x; uint y; 作为数据,并且几乎将数学可以做的所有事情都定义为方法。其中一些方法可能很大。 (复制)构造函数只是初始化两个数据成员。

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,将它们填充到一个向量中并使用它们。 (比如说,所有方法都被调用一次) 这只是一个愚蠢的例子!

它会比没有方法但有很多实用功能的同一个类慢吗?我绝不是在谈论虚函数!

我的动机:我经常发现自己编写了不错且相对强大的类,但是当我必须像上面的示例中那样初始化/使用大量它们时,我会感到紧张。 我想我不应该。

我认为我知道的是:

  1. 方法在内存中只存在一次。 (优化除外)
  2. 分配 只发生在数据 成员,他们是唯一的东西 复制。

所以没关系。我错过了什么吗?

【问题讨论】:

标签: c++ performance


【解决方案1】:

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

当然,分配时只考虑数据成员,嗯,继承可能会在对象大小中为 vptrs 引入一些额外的 ptr,但不是什么大问题

【讨论】:

    【解决方案2】:

    您已经获得了一些非常好的技术建议。我想抛出一些非技术性的东西:正如 STL 向我们展示的那样,在成员函数中完成这一切可能不是最好的方法。我没有堆积论据,而是参考 Scott Meyers 关于该主题的课堂文章:How Non-Member Functions Improve Encapsulation

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

    【讨论】:

    • 您指出了一篇有趣的文章。但我担心的是,这些人的代码似乎与大多数正常人大不相同。我喜欢成员函数,因为在每个值得注意的 IDE 中,它们都会向我提出并告诉我该类可以做什么。这对于非会员非朋友功能来说是不可能的。
    • 如果一个免费函数不能告诉你它的作用,那么它需要一个更好的名字。
    • 不是我的意思。 IDE 提供了 class. 以减少手册中的缓慢查找。那里不显示免费功能。我可以搜索它们,但这比输入“。”要麻烦得多。并等待半秒。这就是为什么人们只看“坏风格”的类库而没有大量模板魔术和分散接口的原因之一。每个登上提升精通之山的人都可以为他的成就感到自豪,但只能羡慕那些仅从上下文相关工具提示中的信息即可使用而无需大惊小怪的库。
    • 在这里,当您键入 namespace_name:: 时,IDE 将查找并建议命名空间成员,就像在键入 class_name.class_name->class_name:: 时对类成员所做的那样。
    【解决方案3】:

    我想这比您正在寻找的答案更多,但这里是......

    SO 充满了人们担心 X、Y 或 Z 性能的问题,而这种担心是一种猜测

    如果您担心某事的性能,请不要担心,了解

    这是怎么做的:

    1. 编写程序

    2. Performance tune it

    3. 从经验中学习

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

    • 最佳实践表明不要过早优化

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

    • 性能调优揭示了时间的去向,即:泛泛而谈,从小山丘中拔地而起,调用函数和属性而不知道它们需要多长时间,并使用指数时间在多个层上执行此操作。

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

    所以在某种程度上,最佳实践告诉我们:过早优化。明白这点?它说“不要担心性能”,它说“担心性能”,它导致我们尝试不成功的事情不担心。而且我们越是担心它,根据我们更好的判断,它会变得越糟。

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

    【讨论】:

      【解决方案4】:

      如果你真的很担心,你可以告诉你的编译器内联构造函数。这个优化步骤应该给你留下干净的代码和干净的执行。

      【讨论】:

        【解决方案5】:

        这 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 对应于你实际拥有的每个像素 3,额外的一个是指向虚拟表的指针。

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

        【讨论】:

          【解决方案6】:

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

          类的方法有隐藏的 this 指针,但你不必担心。大多数时候编译器会尝试通过寄存器来传递它。

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

          短:

          • 您不经常创建/销毁的对象可以具有虚拟方法、继承等,只要它有利于设计。
          • 您经常创建/销毁的对象应该很小(很少的数据成员)并且不应该有很多虚拟方法(最好根本没有 - 性能方面)。
          • 尝试内联小方法/构造函数。这将减少开销。
          • 如果您没有达到所需的性能,请进行简洁的设计和重构。

          对于具有大接口或小接口的类有不同的讨论(例如在 Scott Meyers (More) Effective C++ Books 之一中 - 他选择了最小接口)。但这与性能无关。

          【讨论】:

          • 认为您的第二点是做而不是不做。同样在您的第二点中,我认为虚拟方法很少没有优势,虚拟调用速度较慢,因此调用大量虚拟方法会产生一些(小)性能问题。
          • 谢谢你——你当然是对的。万一你有几个virt。方法,大多数方法调用是直接的(没有对 vtbl 的间接调用)。如果您设法在没有任何 virt 的情况下设计您的课程。函数,构建过程中无需设置vtbl。
          【解决方案7】:

          我同意上述 cmets wrt:performance 和 class layout,并想添加尚未说明的关于设计的评论。

          在我看来,您过度使用 Point 类超出了它的实际设计范围。当然,它可以那样使用,但是应该吗?

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

          这还允许您以更简洁的方式针对重要的情况进行性能优化,而不会降低基本代码的可维护性。

          理论上,我确信有一种巧妙的方法可以使用模板代码、具体类设计等的复杂组合,并获得几乎相同的运行时效率……但我通常不愿意以实现复杂性为代价。

          【讨论】:

            【解决方案8】:

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

            【讨论】:

              【解决方案9】:

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多