Temporary variables and performance in C ++

Suppose we have two functions:

int f(); int g(); 

I want to get the sum of f () and g ().

First way:

 int fRes = f(); int gRes = g(); int sum = fRes + gRes; 

The second way:

 int sum = f() + g(); 

Will there be a difference in performance in these two cases?

The same question for complex types instead of ints

EDIT

As I understand it, I should not worry about performance in such a case (in every situation, including frequently performed tasks) and use temporary variables to increase readability and simplify the code?

+4
source share
7 answers

You can answer questions such as this for yourself by compiling into assembly language (with optimization, of course) and checking the output. If I prepare your example for a complete, compiled program ...

 extern int f(); extern int g(); int direct() { return f() + g(); } int indirect() { int F = f(); int G = g(); return F + G; } 

and compile it ( g++ -S -O2 -fomit-frame-pointer -fno-exceptions test.cc ; the last two switches eliminate a bunch of distractions from the output), I get this (further distractions are removed):

 __Z8indirectv: pushq %rbx call __Z1fv movl %eax, %ebx call __Z1gv addl %ebx, %eax popq %rbx ret __Z6directv: pushq %rbx call __Z1fv movl %eax, %ebx call __Z1gv addl %ebx, %eax popq %rbx ret 

As you can see, the code generated for both functions is identical, so there is no answer to your question, there will be no difference in performance. Now consider complex numbers β€” the same code, but s/int/std::complex<double>/g all over and #include <complex> at the top; the same compilation keys -

 __Z8indirectv: subq $72, %rsp call __Z1fv movsd %xmm0, (%rsp) movsd %xmm1, 8(%rsp) movq (%rsp), %rax movq %rax, 48(%rsp) movq 8(%rsp), %rax movq %rax, 56(%rsp) call __Z1gv movsd %xmm0, (%rsp) movsd %xmm1, 8(%rsp) movq (%rsp), %rax movq %rax, 32(%rsp) movq 8(%rsp), %rax movq %rax, 40(%rsp) movsd 48(%rsp), %xmm0 addsd 32(%rsp), %xmm0 movsd 56(%rsp), %xmm1 addsd 40(%rsp), %xmm1 addq $72, %rsp ret __Z6directv: subq $72, %rsp call __Z1gv movsd %xmm0, (%rsp) movsd %xmm1, 8(%rsp) movq (%rsp), %rax movq %rax, 32(%rsp) movq 8(%rsp), %rax movq %rax, 40(%rsp) call __Z1fv movsd %xmm0, (%rsp) movsd %xmm1, 8(%rsp) movq (%rsp), %rax movq %rax, 48(%rsp) movq 8(%rsp), %rax movq %rax, 56(%rsp) movsd 48(%rsp), %xmm0 addsd 32(%rsp), %xmm0 movsd 56(%rsp), %xmm1 addsd 40(%rsp), %xmm1 addq $72, %rsp ret 

This is a lot more instructions, and the compiler does not do the ideal optimization task, it seems, but nevertheless the code generated for both functions is identical.

+9
source

I think in the second case, it is assigned to a temporary variable when the function still returns a value. However, this becomes somewhat significant when you need to use the values ​​from f() and g() more than once in the case when storing them in a variable instead of recounting them each time can help.

+4
source

If optimization is disabled, it will probably be. If you enable it, they will most likely lead to an identical code. This is especially true for marking fRes and gRes as const .

Because it is finished for the compiler to strip the copy constructor call, if fRes and gRes are complex types , they will not differ in performance for complex types.

Someone mentioned using fRes and gRes more than once. And, of course, this seems to be potentially less optimal, since you would have to call f() or g() more than once.

+2
source

As you wrote it, there is only a subtle difference (in another answer, where there is a sequence point in one and the other).

They would be different if you did this instead:

 int fRes; int gRes; fRes = f(); fRes = g(); int sum = fRes + gRes; 

(Imagine that int is actually some other type with a non-trivial default constructor.)

In this case, you call the default constructors, and then the assignment operators, which potentially work more.

+2
source

It all depends on what kind of optimizations the compiler performs. These two can compile slightly different or exactly the same bytecode. Even if a little different, you cannot measure the statistically significant difference in time and space for these specific samples.

+1
source

On my platform with full optimization turned on, a function that returns sum from both different cases compiled to exactly the same machine code.

The only slight difference between the two examples is that the first guarantees the order in which f() and g() are called, so theoretically the second allows the compiler a little more flexibility. Regardless of whether it matters, it will depend on what f() and g() are really doing, and possibly whether they can be embedded.

+1
source

There is a slight difference between the two examples. There is no sequence point in the expression f() + g() , whereas when calls are made in different statements, there are sequence points at the end of each statement.

The absence of a sequence point means that the order, which these two functions call, is not specified, they can be called in any order, which can help the compiler optimize it.

0
source

Source: https://habr.com/ru/post/1340702/


All Articles