I argued with a friend the other day about those two snippets. Which is faster and why ?
value = 5; if (condition) { value = 6; }
and:
if (condition) { value = 6; } else { value = 5; }
What if value
is a matrix ?
Note: I know that value = condition ? 6 : 5;
exists and I expect it to be faster, but it wasn't an option.
Edit (requested by staff since question is on hold at the moment):
In general, "else if" style can be faster because in the series of ifs, every condition is checked one after the other; in an "else if" chain, once one condition is matched, the rest are bypassed.
The if-else statement allows the programmer to do exactly that with their code. A condition check is done. If it is true, control goes to one block of code and if it isn't, then control goes to a different block of code defined in else.
Conclusion: If only is faster than If-Else construct.
A WHILE statement loop will execute much faster than an IF statement loop in applications where the loop is placed many commands into a program. Consider, for example, a loop placed at the end of a very long program.
TL;DR: In unoptimized code, if
without else
seems irrelevantly more efficient but with even the most basic level of optimization enabled the code is basically rewritten to value = condition + 5
.
I gave it a try and generated the assembly for the following code:
int ifonly(bool condition, int value) { value = 5; if (condition) { value = 6; } return value; } int ifelse(bool condition, int value) { if (condition) { value = 6; } else { value = 5; } return value; }
On gcc 6.3 with optimizations disabled (-O0
), the relevant difference is:
mov DWORD PTR [rbp-8], 5 cmp BYTE PTR [rbp-4], 0 je .L2 mov DWORD PTR [rbp-8], 6 .L2: mov eax, DWORD PTR [rbp-8]
for ifonly
, while ifelse
has
cmp BYTE PTR [rbp-4], 0 je .L5 mov DWORD PTR [rbp-8], 6 jmp .L6 .L5: mov DWORD PTR [rbp-8], 5 .L6: mov eax, DWORD PTR [rbp-8]
The latter looks slightly less efficient because it has an extra jump but both have at least two and at most three assignments so unless you really need to squeeze every last drop of performance (hint: unless you are working on a space shuttle you don't, and even then you probably don't) the difference won't be noticeable.
However, even with the lowest optimization level (-O1
) both functions reduce to the same:
test dil, dil setne al movzx eax, al add eax, 5
which is basically the equivalent of
return 5 + condition;
assuming condition
is zero or one. Higher optimization levels don't really change the output, except they manage to avoid the movzx
by efficiently zeroing out the EAX
register at the start.
Disclaimer: You probably shouldn't write 5 + condition
yourself (even though the standard guarantees that converting true
to an integer type gives 1
) because your intent might not be immediately obvious to people reading your code (which may include your future self). The point of this code is to show that what the compiler produces in both cases is (practically) identical. Ciprian Tomoiaga states it quite well in the comments:
a human's job is to write code for humans and let the compiler write code for the machine.
The answer from CompuChip shows that for int
they both are optimized to the same assembly, so it doesn't matter.
What if value is a matrix ?
I will interpret this in a more general way, i.e. what if value
is of a type whose constructions and assignments are expensive (and moves are cheap).
then
T value = init1; if (condition) value = init2;
is sub-optimal because in case condition
is true, you do the unnecessary initialization to init1
and then you do the copy assignment.
T value; if (condition) value = init2; else value = init3;
This is better. But still sub-optimal if default construction is expensive and if copy construction is more expensive then initialization.
You have the conditional operator solution which is good:
T value = condition ? init1 : init2;
Or, if you don't like the conditional operator, you can create a helper function like this:
T create(bool condition) { if (condition) return {init1}; else return {init2}; } T value = create(condition);
Depending on what init1
and init2
are you can also consider this:
auto final_init = condition ? init1 : init2; T value = final_init;
But again I must emphasize that this is relevant only when construction and assignments are really expensive for the given type. And even then, only by profiling you know for sure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With