Introduction
You might think that this is a stupid debate, and maybe you are right but let me take a moment to explain to you if we should care whether to return true or false.
True and false are the basis in computer programming and traditional logic. The binary system is used based on this only two possibilities, the value is either one or zero.
To understand if returning true or false has an impact in our code performance we have to talk about how computers work in a very low level, in specific I’m going to talk a bit about x86-64 assembly and different C++ compilers.
Benchmarking
Once we compile our code in C++ the compilers convert the code into assembly code. In assembly we usually talk about number of instructions instead of number of lines. And even different instructions can take different number of cycles to be processed in our CPU. Okey, let’s see how return true and return false is compiled into assembly code using GCC 11.2 and Clang 12.0.1. Both without any optimizations on.
Clang 12.0.1 -O0
/*
push rbp
mov rbp, rsp
xor eax, eax
and al, 1
movzx eax, al
pop rbp
ret
*/
bool ReturnFalse() {
return false;
}
/*
push rbp
mov rbp, rsp
mov al, 1
and al, 1
movzx eax, al
pop rbp
ret
*/
bool ReturnTrue(){
return true;
}
GCC 11.2 -O0
/*
push rbp
mov rbp, rsp
mov eax, 0
pop rbp
ret
*/
bool ReturnFalse() {
return false;
}
/*
push rbp
mov rbp, rsp
mov eax, 1
pop rbp
ret
*/
bool ReturnTrue(){
return true;
}
As we can see Clang takes 4 more instructions than GCC to do the same piece of code. Now let’s take a look what happens when we turn optimizations on (-O3) in both compilers.
Clang 12.0.1 -O3
/*
xor eax, eax
ret
*/
bool ReturnFalse() {
return false;
}
/*
mov al, 1
ret
*/
bool ReturnTrue(){
return true;
}
GCC 11.2 -O3
/*
xor eax, eax
ret
*/
bool ReturnFalse() {
return false;
}
/*
mov eax, 1
ret
*/
bool ReturnTrue(){
return true;
}
Now both compilers are able to perform both tasks in only 2 instructions. But as I mention before all instructions are not the same, let’s take a moment to analize this in machine code.
The instruction mov definitions is: “Copies the second operand (source operand) to the first operand (destination operand)”. Wich means that we are copying the right value to the left register. And its translation to machine code is:
mov eax, 1 # b8 01 00 00 00
mov al, 1 # b0 01
Why are the machine code different if both instructions perform the same operation? This is because copying a value into a register depends on the register. In this case eax is a 32 bits register meanwhile al is an 8 bit subset of eax register.
On the other hand, the xor definition says: “Performs a bitwise exclusive OR (XOR) operation on the destination (first) and source (second) operands and stores the result in the destination operand location“. A classic XOR logic operation which has the advantage that if done with the register itself it will always become 0. The CPU is extremely optimized to perform this kind of bitwise operations.
Conclusion
Returning 0 (false) seems to be better than returning 1 (true) but the difference it is almost indistinguishable. Even thought it is always a good practice to analize your problem and try to optimize it as much as you can, e.g: You can not always return false instead of true, what you can is to study the percentage of times your result will be true or false. This is a common practice when developing in assembly code specially when you have to deal with some if-else
and you really want to just ride of the evaluations as fast as possible.