简体   繁体   中英

Can the C++ compiler optimize away the use of dummy variables that are used to pass the results of functions to another function?

In the code snippet below:

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

Can the creation of the dummy variables 'a' and 'b' with the results of fxn1 and fxn2 be optimized away by the compiler so that the results of fxn1 and fxn2 are passed directly as arguments to fxn3?

I realize that the code could easily be written as

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

to accomplish the same intent. However, IMO this would become very clunky if more arguments are involved and there are more dependencies between functions.

I am aware of the lazy evaluation approach but that would involve more code than I would like to introduce. Thx

In theory, yes, it is possible. If you write the one-liners, you also get temporaries that are modeled by the compiler. The difference in intend sits in the lifetime and the destructor. When your compiler can prove the lifetime doesn't matter, it can do so.

In practice, you'll need to ensure the compiler can see what's going on, either be enabling link time optimization (LTO) or by having the code inlined. Having custom destructors in the classes A and B or one of its members doesn't help, especially when in another CPP file (if you don't have LTO)

One can wonder if it even makes sense to worry about these elements. Often it makes more sense to profile your code and look at where you loose the most time.

When it does seem to be part of the hot path, check the generated assembly. Compiler explorer is a very good online tool to help you with that.

Why not simply doing it? The following code:

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;
}   

Results to:

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  

What you see is:

All your functions are reduced to a single constant load:

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

Which is simply "-3".

The rest of main is only for using std::cout .

So your question:

Can the creation of the dummy variables 'a' and 'b' with the results of fxn1 and fxn2 be optimized away by the compiler so that the results of fxn1 and fxn2 are passed directly as arguments to fxn3?

Is simply "YES" :-) It "can" be but it is not a must. Especially if your code depends on more variables and produces side effects, it will maybe not evaluated to a const var. So you have always to measure and check it out for your own real code!

I realize that the code could easily be written as

warning, with

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

you are sure fxn1(x1, x2, ...) is executed before fxn2(y1, y2, ...) , but with C c = fxn3(fxn1(x1, x2, ...), fxn2(y1, y2, ...)); the order of the execution of the two arguments is indeterminate (if I am not wrong)

So these two forms aren't equivalent except if there is no side effect at all, including in the management of the returned values in case their are instances of classes

Can the creation of the dummy variables 'a' and 'b' with the results of fxn1 and fxn2 be optimized away by the compiler so that the results of fxn1 and fxn2 are passed directly as arguments to fxn3 ?

If the local variables a and b are only used to b the arguments of fxn3 yes perhaps, but respecting the order of call of fxn1 and fxn2 except if it can know there are no side effect at all, may be also these calls are replaced by their code (managed as inline) etc

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM