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
C++
/*
    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
C++
/*
    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
C++
/*
    xor     eax, eax
    ret
*/
bool ReturnFalse() {
    return false;
}

/*
    mov     al, 1
    ret
*/
bool ReturnTrue(){
    return true;
}
GCC 11.2 -O3
C++
/*
    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:

ShellScript
mov eax, 1 # b8 01 00 00 00
ShellScript
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.

x86-64 register explanation by: https://www.tutorialspoint.com/assembly_programming/assembly_registers.htm

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.

XOR gate output by: https://sites.google.com/a/cecytebc.edu.mx/audg_2014-ii_tics/mecatronica/2bmt-digitales

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.

References

  1. https://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/
  2. https://qastack.mx/programming/33666617/what-is-the-best-way-to-set-a-register-to-zero-in-x86-assembly-xor-mov-or-and
  3. https://godbolt.org/z/reTEPE3En


2 Comments

Peter · September 15, 2023 at 11:53

The values usually have meaning so I think you should return whatever makes sense. 🙂

    RubenRubioM · October 7, 2023 at 19:31

    Yes I agree with you! This is just an analysis about the better performance that can be possible achieved. Whether if this is useful or not is another topic.

Leave a Reply

Your email address will not be published. Required fields are marked *