C ++编译器能否优化使用用于将函数结果传递给另一个函数的虚拟变量?

问题描述 投票:-1回答:3

在下面的代码段中:

A a = fxn1(x1, x2, ...);
B b = fxn2(y1, y2, ...);
C c = fxn3(a, b);

用编译器优化fxn1和fxn2结果的伪变量'a'和'b'的创建是否可以将fxn1和fxn2的结果作为参数直接传递给fxn3?

我意识到代码很容易写成

C c = fxn3(fxn1(x1, x2, ...), fxn2(y1, y2, ...));

实现相同的意图。但是,如果涉及更多参数并且函数之间存在更多依赖关系,IMO将变得非常笨重。

我知道懒惰的评估方法,但这将涉及比我想介绍的更多的代码。谢谢

c++ optimization
3个回答
3
投票

为什么不干脆呢?以下代码:

struct A { int a; };
struct B { int b; };
struct C { int c; };

A fxn1( int x1, int x2 )
{   
    return A{ x1+x2 };
}

B fxn2( int y1, int y2 )
{
    return B{ y1-y2 };
}   

C fxn3( const A& a, const B& b ) 
{   
    return C{ a.a * b.b };
}

int main()
{   
    int x1 = 1;
    int x2 = 2;
    int y1 = 3;
    int y2 = 4;
    A a = fxn1(x1, x2);
    B b = fxn2(y1, y2);
    C c = fxn3(a, b); 

    std::cout << c.c << std::endl;
}   

结果:

0000000000400630 <main>:
  400630:   48 83 ec 08             sub    $0x8,%rsp
  400634:   be fd ff ff ff          mov    $0xfffffffd,%esi
  400639:   bf 60 10 60 00          mov    $0x601060,%edi
  40063e:   e8 cd ff ff ff          callq  400610 <std::basic_ostream<char, std::char_traits<char> >::operator<<(int)@plt>
  400643:   48 89 c7                mov    %rax,%rdi
  400646:   e8 95 ff ff ff          callq  4005e0 <std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)@plt>
  40064b:   31 c0                   xor    %eax,%eax
  40064d:   48 83 c4 08             add    $0x8,%rsp
  400651:   c3                      retq   
  400652:   0f 1f 40 00             nopl   0x0(%rax)
  400656:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40065d:   00 00 00  

你看到的是:

您的所有功能都减少到一个恒定负载:

400634:   be fd ff ff ff          mov    $0xfffffffd,%esi

这简直就是“-3”。

主要的其余部分仅用于使用std::cout

所以你的问题:

用编译器优化fxn1和fxn2结果的伪变量'a'和'b'的创建是否可以将fxn1和fxn2的结果作为参数直接传递给fxn3?

简单地说“是”:-)它“可以”但不是必须的。特别是如果您的代码依赖于更多变量并产生副作用,它可能不会被评估为const var。所以你总是要测量并检查出你自己的真实代码!


4
投票

理论上,是的,这是可能的。如果你编写单行代码,你也可以获得由编译器建模的临时代码。意图的差异在于生命周期和析构函数。当您的编译器可以证明生命周期无关紧要时,它可以这样做。

在实践中,您需要确保编译器可以看到正在发生的事情,要么启用链接时间优化(LTO),要么让代码内联。在A和B类或其中一个成员中使用自定义析构函数没有帮助,尤其是在另一个CPP文件中时(如果您没有LTO)

人们可能想知道担心这些元素是否有意义。通常,对代码进行概要分析并查看放松时间最多的位置更有意义。

当它似乎是热路径的一部分时,检查生成的程序集。编译器资源管理器是一个非常好的在线工具,可以帮助您。


2
投票

我意识到代码很容易写成

警告,用

A a = fxn1(x1, x2, ...);
B b = fxn2(y1, y2, ...);
C c = fxn3(a, b);

你确定fxn1(x1,x2,...)在fxn2(y1,y2,...)之前执行,但是对于C c = fxn3(fxn1(x1, x2, ...), fxn2(y1, y2, ...));,两个参数的执行顺序是不确定的(如果我没错)

所以这两种形式不相同,除非根本没有副作用,包括在返回值的管理中,如果它们是类的实例

用编译器优化fxn1和fxn2结果的伪变量'a'和'b'的创建是否可以将fxn1和fxn2的结果作为参数直接传递给fxn3?

如果局部变量a和b仅用于参数fxn3也许是,但是尊重fxn1和fxn2的调用顺序,除非它可以知道根本没有副作用,也可能是这些调用被替换为他们的代码(作为内联管理)等

© www.soinside.com 2019 - 2024. All rights reserved.