我们应该为函数的返回值使用临时变量吗?

时间:2012-12-19 16:53:16

标签: c++ performance

我想到了这些:这两种做法是否有性能差异:

  1. 将函数的返回值存储在临时变量中 将该变量作为参数提供给另一个函数。
  2. 将该功能放入另一个功能中。
  3. 规范

    假设所有类和函数都写得正确。

    案例1。

    ClassA a = function1();
    ClassB b = function2(a);
    function3(b);
    

    案例2。

    function3(function2(function1()));
    

    我知道只有一次运行没有太大区别,但我认为我们可以在循环中运行很多次,我创建了一些测试。

    测试

    #include <iostream>
    #include <ctime>
    #include <math.h>
    using namespace std;
    
    int main()
    {
       clock_t start = clock();
       clock_t ends = clock();
    
       // Case 1.
       start = clock();
       for (int i=0; i<10000000; i++)
       {
          double a = cos(1);
          double b = pow(a, 2);
          sqrt(b);
       }
       ends = clock();
       cout << (double) (ends - start) / CLOCKS_PER_SEC << endl;
    
       // Case 2.
       start = clock();
       for (int i=0; i<10000000; i++)
          sqrt(pow(cos(1),2));
       ends = clock();
       cout << (double) (ends - start) / CLOCKS_PER_SEC << endl;
       return 0;
    }
    

    结果

    • 案例1 = 6.375
    • 案例2 = 0.031

    为什么第一个更慢,如果第二个更快,为什么我们不总是这样编写代码?无论如何,第二个实践是否有名称?
    我也想知道如果我在第一种情况下在for循环之外创建变量会发生什么,但结果是相同的。为什么呢?

2 个答案:

答案 0 :(得分:4)

如果您想要计算紧缩并且您的数字变得更加一致,那么打破这种全能优化。确保代码获得正确的值实际上是运行而不是完全抛弃,我已将两个测试中的结果分配给一个易变的本地(这不是完全正确的使用不稳定,但确保只有值 - 创建是一个重要的增量确实很好。

#include <iostream>
#include <ctime>
#include <cmath>
using namespace std;

int main()
{
    clock_t start;
    volatile double val;

    for (int j=1;j<=10;j++)
    {
        // Case 1.
        start = clock();
        for (int i=0; i<2000000; i++)
        {
            double a = cos(1);
            double b = pow(a, 2);
            val = sqrt(b);
        }
        cout << j << ':' << (double) (clock() - start) / CLOCKS_PER_SEC << endl;

        // Case 2.
        start = clock();
        for (int i=0; i<2000000; i++)
            val = sqrt(pow(cos(1),2));
        cout << j << ':' << (double) (clock() - start) / CLOCKS_PER_SEC << endl << endl;
    }
    return 0;
}

在我的Macbook Air上生成以下版本编译输出(任何时候都不是速度恶魔):

1:0.001465
1:0.001305

2:0.001292
2:0.001424

3:0.001297
3:0.001351

4:0.001366
4:0.001342

5:0.001196
5:0.001376

6:0.001341
6:0.001303

7:0.001396
7:0.001422

8:0.001429
8:0.001427

9:0.001408
9:0.001398

10:0.001317
10:0.001353

答案 1 :(得分:0)

上述两个循环的正确和合法的完全优化是“甚至不做循环”。您可以很容易地看到在第一种情况下使用未初始化的变量使编译器混淆的情况,或者您使用变量会使其混淆,或者您的优化级别强制命名变量实际存在。

现在C ++ 11中的两者之间存在差异,涉及临时变量的隐式移动,但您可以使用std::move来解决此问题。 (我不确定,但是最后一次使用超出范围的局部变量可能有资格进行隐式移动)。对于double这不是一个区别,但对于更复杂的类型,这可能是。