I'm trying to evaluate if there's a performance hit in writing this expression
bool func() {
if (expr1 || expr2 ... || exprN)
return 1;
return 0;
}
as
bool func() {
if (expr1)
return 1;
if (expr2)
return 1;
...
if (exprN)
return 1;
return 0;
}
The reason I'm trying to do the latter is simply to improve readability/maintainability (eg. latter can be written in terms of a helper macro and thereby making it easier to add/remove exprs. There are about 50 expressions in this case).
A similar scenario is writing
bool func() {
if (expr1 && expr2 && ... && exprN) {
return 1;
}
return 0;
}
as
bool func() {
if (!expr1) {
return 0;
}
if (!expr2) {
return 0;
}
...
if (!exprN) {
return 0;
}
return 1;
}
Is there a performance hit in this case and if so do compilers optimize try to optimize it? I'd be interested to know if gcc does that.
(To give some context the expressions themselves are functions and let's say we want to determine if all or at least one returns true. The functions take arguments of different types)
Your two versions are functionally equivalent.
In C/C++ (and many other languages) the logical operators || and && perform "short-circuit" evaluation. They evaluate the sub-expressions from left to right, stopping as soon as the result is known. In the case of ||, this means it stops as soon as it gets a true (non-zero) result (because true || anything is true); in the case of &&, it stops as soon as it gets a false (zero) result (because false && anything is false).
If you were performing any other actions in your if bodies other than just returning from the function, it would be duplicated for every expression that's true. But since you're returning from the function as soon as one of the expressions is true, you'll never test any of the remaining expressions. So it's doing the same short-circuiting.
If you did have some action in the body, you could get the same type of short-circuiting by using else if rather than if. However, this would violate the DRY principle. But if it's in the output of a macro, rather than the original source code, this wouldn't be a signficant concern (although it would likely increase the size of the generated code -- if taken to extremes this could have memory performance implications).
Your current code
bool func() {
if (expr1 || expr2 ... || exprN)
return 1;
return 0;
}
involving umpteen logical expressions, can be written as the possibly more clear & maintainable
auto func()
-> bool
{
return false
|| expr1
|| expr2 ...
|| exprN;
}
This allows the compiler the best opportunity to optimize, because it's presented with complete information about the desired effect.
In contrast, a series of individual if statements would have to be analyzed by the compiler to determine that they implement an OR-chain.
As always when in doubt about micro-performance, MEASURE.
The C and C++ codes are generated to its assembly equivalent.
Hence, what i think is that the code generated should not be different unless you have multiple output function or jump statements(including Return, like you have in your second example).
So i think there should be no performance hit unless you have too many jumps in your improved readability code.
Related
Will judge_function_2 be faster than judge_function_1? I think judge_function_1's AND statement needs to judge two, while judge_function_2 only needs to be judged once.
#include <iostream>
using namespace std;
bool judge_function_1()
{
if(100 == 100 || 100 == 200)
{
return true;
}
return false;
}
bool judge_function_2()
{
if(100 == 100)
{
return true;
}
if(100 == 200)
{
return true;
}
return false;
}
int main()
{
cout << judge_function_1() << endl;
cout << judge_function_2() << endl;
return 0;
}
Using godbolt and compiling with gcc with optimizations enabled results in the following assembly code https://godbolt.org/z/YEfYGv5vh :
judge_function_1():
mov eax, 1
ret
judge_function_2():
mov eax, 1
ret
The functions assembly code is identical and both return true, they will be exactly the same fast.
Compilers know how to read and understand code. They cannot guess your intentions, but thats only an issue when your code does not express your intentions.
A compiler "knows" that 100 == 100 is always true. It cannot be something else.
A compiler "knows" that true || whatever is always true. It cannot be something else.
A compiler "knows" that after a return no other statements are executed in a function.
The transformations are straightforward to proove that both functions are equivalent to
void same_thing_just_with_less_fluff() { return true; }
The code in both functions is just a very complicated way to say return true; / "this function returns true".
Compilers optimize code when you ask them to. If you turn on optimizations there is no reason to expect any difference between the two functions. Calling them has exactly the same effect. There is no observable difference between the two. And as shown above, the way to proove this is rather simple. It is somewhat safe to assume that any compiler can optimize the two functions to simply return true;.
To my experience, a misunderstanding that is common among beginners is that your code would be instructions for your CPU. That when you write 100 == 100 then at runtime there must be 100 in one register and 100 in another register and the cpu needs to carry out an operation to check if the values are the same. This picture is totally wrong.
Your code is an abstract description of the observable behavior of your code. Its a compilers job to translate this abstract description into something your cpu understands and can execute to exhibit the observable behavior you described in your code in accordance with the definitions provided by the C++ standard.
What your code describes is in plain english: Write 1 followed by a newline to stdout and then flush it twice.
From your guess:
I think judge_function_1's AND statement needs to judge two, while judge_function_2 only needs to be judged once.
I deduce that there ARE some compare performed, like if you pass two parameters to your function and compare them with some constant:
bool judge_function_1(int x, int y)
{
if(x == 100 || y == 200)
{
return true;
}
return false;
}
Even in that case, if the first condition is true, the function will return immediately, without comparing y to 200.
I'm learning c++ and I've run into some behaviour that I can't explain. The two pieces of code below provide different results, while I would expect them to be equivalent:
success = true;
vector<instruction>::const_iterator i;
for (i = instructions.begin(); i != instructions.end(); ++i) {
bool up = update(*i);
success = success && up;
}
and
success = true;
vector<instruction>::const_iterator i;
for (i = instructions.begin(); i != instructions.end(); ++i) {
success = success && update(*i);
}
I have the impression that the second version always takes the initial value of the iterator. Could someone explain me the reason?
The two pieces of code are not equivalent.
The first one always calls update, while the second one will not. The reason is that && does something called short-circuit boolean evaluation. If success is false, the second half of the expression is not evaluated.
Note that you did not post what update is or what is returned. Therefore we can only assume what may be able to be returned from update, which is either going to be true or false.
If you wanted update to always be called, it should be placed first in the && expression.
The operator && will not evaluate the right hand side if the result is computable with just the left hand side value. We call this short-circuiting.
Once success is false, update(*i) will no longer be called.
Aside from your first block of code (which always calls update(*i)), one fix is to use the operator & instead which always evaluates both arguments. Another fix is to write
success = update(*i) && success;
But that's vulnerable to recalcitrant refactoring. Better still, use
success &= update(*i);
But be aware of this comment by #Angew:
Using &= in a boolean context is dangerous. What if update now (or after a future refactoring) returns an int with the semantics "non-zero for true" (cf. isdigit and friends)? Remember that bool(true & 2) is false.
I've been familiar with the ternary operator for quite some time now, and have worked with it in a few differnet languages. My understanding of the operator is this:
condition ? expr1 : expr2
However, in C++, the following code is legal:
int i = 45;
(i > 0) ? i-- : 1;
Aren't you, in effect, just writing 1; or i - 1;How is this a complete statement? I understand that the intention of the code is to decrement i if it's greater than 0, but I would've thought that the code would generate a compiler error as just being an expression, not a full statement. I expected code like this:
int i = 45;
i = (i > 0) ? i - 1 : i;
This is called expression statement. The expression is evaluated and its value is discarded.
Even this is valid:
42;
although it does nothing. Only side effects (like i--, assignment, etc) in the expression have effects.
In fact, many statements we use are expression statements: assignments, function calls, etc:
a = 42;
foo();
That is a valid expression. You might have received a warning because you are not saving the result of the expression, but that you have the i-- your statement does have an effect.
In C++, an expression like 1 is a perfectly valid statement with no side effects. You could very feasibly write this function:
void f() {
1;
}
In fact, even this is correct.
void f() {
;;;;
}
A literal statement evaluates its arguments but does nothing more. The system views 1; as being just like func();. The only difference is that while func(); would logically have some side effects, 1; does not so it ends up being a no-op. The ternary operator evaluates like an if-statement, so the second form is only evaluated if the operand is true. Thus:
(i > 0) ? i-- : 1;
If i is greater than 0, the second form is evaluated. When it is evaluated, it carries its side effect, which decrements i by 1. Otherwise, the third form is evaluated, which does nothing. Although this block of code works, it is not incredibly readable, so while it's nice toy code a real if-statement is ideal for situations like this. For the same reason, this line would have the same effect but be frowned upon for being equally unreadable.
((i > 0) && (i--)) || 1;
Assuming you didn't overwrite the boolean operators, this code will short-circuit and behave like the ternary operator. If i is not greater than 0, then the && need not evaluate its second operand since the && is false, but the || must since it might be true. Inversely, if i is greater than 0, the && needs to evaluate but the || already knows it's true.
Aren't you, in effect, just writing 1; or i - 1;
No: i-- is not the same as i - 1. In the first case, the value of i is modified. In the second case it is not.
In the event that i less than or equal to zero, then you're correct that the resulting 'code' will be 1. However, the compiler will realise that this is not a useful thing to execute and so it ought to generate code equivalent to:
if( i > 0 ) i--;
Some (including myself) would consider that using the ternary operator in this fashion is bad style. Just for fun, here's another way someone might write it that's also not very nice (also more likely to generate compiler warning):
i > 0 && i--;
In the end, style is a matter of preference. The compiler, for the most part, will decide the best way to turn your code into assembly. So you owe it to yourself to write code that is clear and concise.
Say, I have a function like shown below
void caller()
{
int flag = _getFlagFromConfig();
//this is a flag, which according to the implementation
//is supposed to have only two values, 0 and 1 (as of now)
callee_1(flag);
callee_2(1 == flag);
}
void callee_1(int flag)
{
if (1 == flag)
{
//do operation X
}
}
void callee_2(bool flag)
{
if (flag)
{
//do operation X
}
}
Which of the callee functions will be a better implementation?
I have gone through this link and I'm pretty convinced that there is not much of a performance impact by taking bool for comparison in an if-condition. But in my case, I have the flag as an integer. In this case, is it worth going for the second callee?
It won't make any difference in terms of performance, however in terms of readability if there are only 2 values then a bool makes more sense. Especially if you name your flag something sensible like isFlagSet.
In terms of efficiency, they should be the same.
Note however that they don't do the same thing - you can pass something other than 1 to the first function, and the condition will evaluate to false even if the parameter is not itself false. The extra comparison could account for some overhead, probably not.
So let's assume the following case:
void callee_1(int flag)
{
if (flag)
{
//do operation X
}
}
void callee_2(bool flag)
{
if (flag)
{
//do operation X
}
}
In this case, technically the first variant would be faster, since bool values aren't checked directly for true or false, but promoted to a word-sized type and then checked for 0. Although the assembly generated might be the same, the processor theoretically does more work on the bool option.
If the value or argument is being used as a boolean, declare it bool.
The probability of it making any difference in performance is almost 0,
and the use of bool documents your intent, both to the reader and to
the compiler.
Also, if you have an int which is being used as a flag (due to an
existing interface): either use the implicit conversion (if the
interface documents it as a boolean), or compare it with 0 (not with
1). This is conform with the older definitions of how int served as
a boolean (before the days when C++ had bool).
One case where the difference between bool and int results in different (optimized) asm is the negation operator ("!").
For "!b", If b is a bool, the compiler can assume that the integer value is either 1 or 0, and the negation can thus be a simple "b XOR 1". OTOH, if b is an integer, the compiler, barring data-flow analysis, must assume that the variable may contain any integer value, and thus to implement the negation it must generate code such as
(b != 0) ? 0: 1
That being said, code where negation is a performance-critical operation is quite rare, I'm sure.
If i use hte following codes , will the compiler optimize it like a switch structure , which uses a binary tree to search for values ?
if ( X == "aaaa" || X == "bbbb" || X == "cccc" || X == "dddd" )
{
}
else if ( X == "abcd" || X == "cdef" || X == "qqqq" )
{
}
It's just an example , there's no pattern of what's inside the quote symbol
UPDATE
Ok , X is a string , but i don't really think it matters here , i just want to know , when everything inside the if was all about single variable , will it be optimized.
The values WILL be compared one after the other as it is a requirement of the || or, the short-circuit operator. So, here two things will happen:
X will be compared one-by-one from right-to-left.
There will be NO MORE comparisons after any comparison that succeeds (Since it is the short-circuit OR operator) i.e. in the following case
For example:
int hello() {
std::cout<<"Hello";
return 10;
}
int world() {
std::cout<<"World";
return 11;
}
int hello2() {
std::cout<<"Hello2";
return 9;
}
int a = 10;
bool dec = (a == hello() || a == world())
bool dec = (a == hello2() || a == hello() || a == world())
The output for the first statement will be:
Hello
as a == world() will not be executed, and for the second Hello2 Hello, as the comparisons keep on happening till the first success.
In case of the && operator, the comparisons keep on happening until the first failure (as that is enough to determine the outcome of the entire statement).
Almost certainly not. Binary search requires some sort of ordering
relationship, and an ability to compare for less-than. The compiler
cannot assume such exists, and even if it does find one, it cannot
assume that it defines an equivalence relation which corresponds to
==. It's also possible that the compiler can't determine that the
function defining the ordering relationship has no side effects. (If it
has side effects, or if any operation in the expression has side
effects, the compiler must respect the short circuiting behavior of
||.) Finally, even if the compiler did all this... what happens if I
carefully chose the order of the comparisons so that the most frequent
case is the first. Such an “optimization” could even end up
being a pessimization.
The “correct” to handle this is to create a map, mapping the
strings to pointers to functions (or to polymorphic functional objects,
if some state is involved).
It probably depends on the flags you set. Binary trees are faster for search but usually require more code to be handled. So if you optimized for size it probably wont. I'm not sure if it will do it anyway. You know, gcc is optimized according to a LOT of flags. O1, O2, O3, O4 are just simple forms to indicate big groups of flags. You can find a list of all the optimization flags here: http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html
Try to search for strings, binary trees, etc in that page.
Best way to test this is the example like below.
int a = 0;
int b = 0;
if(a == a || b++) {
...
}
cout << b;
value of the b variable should be 0 at the end. b++ part won't be executed. that's the expected behaviour but there might be some exceptions depending on the compiler and its optimization settings. even many scripting langauges like JavaScript behaves this way.