【问题标题】:Performance when accessing class members访问类成员时的性能
【发布时间】:2010-05-19 16:34:59
【问题描述】:

我正在写一些性能关键的东西,想知道如果我使用它是否会有所作为:

int test( int a, int b, int c )
{
    // Do millions of calculations with a, b, c
}

class myStorage
{
public:
  int a, b, c;
};

int test( myStorage values )
{
   // Do millions of calculations with values.a, values.b, values.c
}
  • 这基本上会导致类似的代码吗?访问类成员是否有额外的开销?

我确信这对于 C++ 专家来说是清楚的,所以我现在不会尝试为其编写不切实际的基准

【问题讨论】:

  • 如果性能很重要,那么除了编写性能测试之外,最终别无选择。
  • 微优化(也许我们甚至可以为其添加标签)
  • @David:那个标签已经存在了。 :)

标签: c++ micro-optimization


【解决方案1】:

编译器可能会均衡它们。如果它有任何大脑,它会将values.avalues.bvalues.c复制到局部变量或寄存器中,这也是简单情况下的情况。

相关格言:

  1. 过早的优化是万恶之源。

  2. 写下来,这样您就可以在六个月后的凌晨 1 点阅读它,并且仍然了解您要做什么。

  3. 大多数时候,重要的优化来自于重构您的算法,而不是变量访问方式的微小变化。是的,我知道有例外,但这可能不是其中之一。

【讨论】:

  • 我认为反对票是因为过于简洁。我已经扩展了答案。
  • +1 表示格言 #2。大多数人不会编写可读的代码,但实际上会评论(描述)操作的意图。
【解决方案2】:

这听起来像是过早的优化。

话虽如此,存在一些差异和机会,但它们会影响对函数的多次调用,而不是函数的性能。

首先,在第二个选项中,您可能希望将 MyStorage 作为常量引用传递。 因此,您的编译代码可能会将单个值推送到堆栈中(以允许您访问容器),而不是推送三个单独的值。如果您有其他字段(除了 a-c),发送 MyStorage 不作为参考实际上可能会花费您更多,因为您将调用复制构造函数并基本上复制所有其他字段。所有这些都是每次调用的费用,而不是函数内的费用。

如果您在函数中使用 b 和 c 进行大量计算,那么您如何传输或访问它们实际上并不重要。如果您通过引用传递,初始成本可能会略高(因为您的对象,如果通过引用传递,可能在堆上而不是堆栈上),但是一旦第一次访问,您机器上的缓存和寄存器可能会意味着低成本访问。如果您按值传递了对象,那么这真的没关系,因为即使最初,这些值也会在堆栈附近。

对于您提供的代码,如果这些是唯一的字段,则可能没有区别。 “values.variable”仅被解释为堆栈中的偏移量,而不是“查找一个对象,然后访问另一个地址”。

当然,如果您不购买这些参数,只需将局部变量定义为函数中的第一步,从对象中复制值,然后使用这些变量。如果您真的多次使用它们,则此副本的初始成本将无关紧要:)

【讨论】:

    【解决方案3】:

    不,你的 cpu 会缓存你一遍又一遍地使用的变量。

    【讨论】:

      【解决方案4】:

      我认为有一些开销,但可能不会太多。因为对象的内存地址会存放在栈中,指向的是堆内存对象,那么就访问实例变量吧。

      如果你把变量 int 存入栈中,那真的会更快,因为值已经在栈中,机器只是去栈中取出来计算:)。

      这还取决于您是否将类的实例变量值存储在堆栈中。如果在 test() 中,你确实喜欢:

      int a = objA.a;
      int b = objA.b;
      int c = objA.c;
      

      我认为这将是几乎相同的性能

      【讨论】:

        【解决方案5】:

        如果您确实在编写性能关键代码,并且您认为一个版本应该比另一个更快,请编写两个版本并测试时序(使用正确的优化开关编译的代码)。您甚至可能希望查看生成的汇编代码。很多事情都会影响非常微妙的代码 sn-ps 的速度,例如寄存器溢出等。

        【讨论】:

          【解决方案6】:

          你也可以用

          开始你的功能
          int & a = values.a;
          int & b = values.b;
          

          虽然编译器应该足够聪明,可以在幕后为你做这件事。一般来说,我更喜欢传递结构或类,这通常会使函数的用途更清楚,而且您不必每次考虑另一个参数时都更改签名。

          【讨论】:

            【解决方案7】:

            与您之前的类似问题一样:它取决于编译器和平台。如果有任何差异,那将是非常小的。

            堆栈上的值和对象中的值通常使用指针(堆栈指针或this 指针)和一些偏移量(函数堆栈帧中的位置,或类内部的位置)来访问.

            以下是一些可能会产生影响的情况:

            • 根据您的平台,堆栈指针可能保存在 CPU 寄存器中,而 this 指针可能不会。如果是这种情况,访问this(可能在堆栈上)将需要额外的内存查找。

            • 内存位置可能不同。如果内存中的对象大于一个缓存行,则这些字段分布在多个缓存行上。仅将相关值放在堆栈帧中可能会提高缓存效率。

            但请注意,我在这里使用“可能”一词的频率。唯一确定的方法就是测量它。

            【讨论】:

              【解决方案8】:

              如果您无法分析程序,请打印出代码片段的汇编语言。

              一般来说,更少的汇编代码意味着更少的执行指令,从而提高性能。这是一种在分析器不可用时粗略估计性能的技术。

              汇编语言列表可让您查看实现之间的差异(如果有)。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2017-01-21
                • 1970-01-01
                • 2021-11-13
                • 1970-01-01
                • 1970-01-01
                • 2018-04-19
                • 2012-10-18
                相关资源
                最近更新 更多