Which is faster: if (bool) or if (int)?

Which value is better to use? Boolean true or Integer 1?

In the topic above, I did some experiments with bool and int in if . So out of curiosity, I wrote this program:

 int f(int i) { if ( i ) return 99; //if(int) else return -99; } int g(bool b) { if ( b ) return 99; //if(bool) else return -99; } int main(){} 

g++ intbool.cpp -S generates asm code for each function as follows:

  • asm code for f(int)

     __Z1fi: LFB0: pushl %ebp LCFI0: movl %esp, %ebp LCFI1: cmpl $0, 8(%ebp) je L2 movl $99, %eax jmp L3 L2: movl $-99, %eax L3: leave LCFI2: ret 
  • asm code for g(bool)

     __Z1gb: LFB1: pushl %ebp LCFI3: movl %esp, %ebp LCFI4: subl $4, %esp LCFI5: movl 8(%ebp), %eax movb %al, -4(%ebp) cmpb $0, -4(%ebp) je L5 movl $99, %eax jmp L6 L5: movl $-99, %eax L6: leave LCFI6: ret 

Amazingly, g(bool) generates more asm instructions! Does this mean that if(bool) bit slower than if(int) ? I thought bool specifically designed for use in a conditional expression such as if , so I expected g(bool) to generate fewer asm instructions, making g(bool) more efficient and faster.

EDIT:

I do not use the optimization flag yet. But even the lack of this, why it generates more asm for g(bool) , is a question for which I am looking for a reasonable answer. I should also tell you that the -O2 optimization flag generates exactly the same asm. But that is not a question. The question is what I asked.




+82
c ++ assembly int boolean
Apr 23 2018-11-11T00:
source share
8 answers

It makes sense to me. Your compiler apparently defines bool as an 8-bit value, and your ABI system requires it to push small (& 32-bit) integer arguments to 32-bit when they are pushed onto the call stack. Therefore, to compare a bool compiler generates code to extract the least significant byte of the 32-bit argument that g receives and compares it with cmpb . In the first example, the int argument uses the full 32 bits that were cmpl stack, so it just compares it all with cmpl .

+90
Apr 23 2018-11-11T00:
source share

Compiling with -03 gives me the following:

F:

  pushl %ebp movl %esp, %ebp cmpl $1, 8(%ebp) popl %ebp sbbl %eax, %eax andb $58, %al addl $99, %eax ret 

g:

  pushl %ebp movl %esp, %ebp cmpb $1, 8(%ebp) popl %ebp sbbl %eax, %eax andb $58, %al addl $99, %eax ret 

.. therefore it compiles essentially the same code, with the exception of cmpl vs cmpb . This means that the difference, if any, does not matter. Judging by the unoptimized code, this is unfair.

Edit to clarify my point. Non-optimized code is for simple debugging, not speed. Comparing the speed of unoptimized code is pointless.

+74
Apr 23 2018-11-11T00:
source share

When I compile this with a reasonable set of parameters (specifically -O3), this is what I get:

For f() :

  .type _Z1fi, @function _Z1fi: .LFB0: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 cmpl $1, %edi sbbl %eax, %eax andb $58, %al addl $99, %eax ret .cfi_endproc 

For g() :

  .type _Z1gb, @function _Z1gb: .LFB1: .cfi_startproc .cfi_personality 0x3,__gxx_personality_v0 cmpb $1, %dil sbbl %eax, %eax andb $58, %al addl $99, %eax ret .cfi_endproc 

They still use different instructions for comparison ( cmpb for boolean vs. cmpl for int), but the rest of the body is identical. A quick look at Intel manuals tells me: ... not much. There is no such thing as cmpb or cmpl . They are all cmp , and I cannot find the time tables at the moment. I assume, however, that there is no difference in frequencies between byte matching and long immediate comparison, so the code is identical for all practical purposes.




edited to add the following based on your addition

The reason that the code in a non-optimized case is different from the other is because it is not optimized. (Yes, itโ€™s circular, I know.) When the compiler walks around AST and generates the code directly, it doesnโ€™t โ€œknowโ€ anything except in the immediate vicinity of AST. At this moment, he lacks all the necessary contextual information to know that at this particular point he can consider the declared bool type as int . A boolean is obviously considered a byte by default, and when manipulating bytes in the Intel world, you should do things like sign-extend to push it to a certain width, to push it on the stack, etc. (You cannot push bytes.)

When the optimizer looks at the AST and does its magic, it looks at the surrounding context and โ€œknowsโ€ when it can replace the code with something more efficient without changing the semantics. Therefore, he โ€œknowsโ€ that he can use an integer in the parameter and thereby lose unnecessary conversions and expansion.

+26
Apr 23 2018-11-11T00:
source share

With GCC 4.5 on Linux and Windows, at least sizeof(bool) == 1 . On x86 and x86_64, you cannot pass less than the general purpose register that the function costs (whether through the stack or register depending on the calling convention, etc.).

Thus, the code for bool, when not optimized, actually reaches a certain length in order to extract that bool value from the argument stack (using another stack slot to store this byte). This is more complicated than just pulling out your own register size variable.

+13
Apr 23 2018-11-11T00:
source share

At the machine level, there is no such thing as a bool

Very few instruction set architectures define any type of type of Boolean operands, although there are often instructions that trigger an action on non-zero values. Typically, for a CPU, this is all one of the scalar types or their strings.

For this compiler and the given ABI, it will be necessary to select specific sizes for int and bool , and when, as in your case, these are different sizes, they can generate slightly different code, and at some optimization levels one can be a little faster.

Why bool one byte on many systems?

It is safer to choose the char type for bool, because someone can create a very large array of them.

Update: "safer", I mean: for compiler and library developers. I am not saying that people need to redefine the type of system.

+8
Apr 23 '11 at 23:24
source share

Yes, the discussion is fun. But just test it:

Test code:

 #include <stdio.h> #include <string.h> int testi(int); int testb(bool); int main (int argc, char* argv[]){ bool valb; int vali; int loops; if( argc < 2 ){ return 2; } valb = (0 != (strcmp(argv[1], "0"))); vali = strcmp(argv[1], "0"); printf("Arg1: %s\n", argv[1]); printf("BArg1: %i\n", valb ? 1 : 0); printf("IArg1: %i\n", vali); for(loops=30000000; loops>0; loops--){ //printf("%i: %i\n", loops, testb(valb=!valb)); printf("%i: %i\n", loops, testi(vali=!vali)); } return valb; } int testi(int val){ if( val ){ return 1; } return 0; } int testb(bool val){ if( val ){ return 1; } return 0; } 

Compiled on a Ubuntu 10.10 64-bit laptop with: g ++ -O3 -o / tmp / test_i / tmp / test_i.cpp

Integer comparison:

 sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m8.203s user 0m8.170s sys 0m0.010s sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m8.056s user 0m8.020s sys 0m0.000s sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m8.116s user 0m8.100s sys 0m0.000s 

Logical test / print without commenting (and integer comment):

 sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m8.254s user 0m8.240s sys 0m0.000s sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m8.028s user 0m8.000s sys 0m0.010s sauer@trogdor:/tmp$ time /tmp/test_i 1 > /dev/null real 0m7.981s user 0m7.900s sys 0m0.050s 

They are the same with 1 assignment and 2 comparisons of each cycle in 30 million cycles. Find something else to optimize. For example, do not use strcmp unless necessary .;)

+5
Apr 24 2018-11-11T00:
source share

It mainly depends on the compiler and optimization. Here's an interesting discussion (agnostic language):

Does "if ([bool] == true)" require one more step than "if ([bool])",

Also check out this post: http://www.linuxquestions.org/questions/programming-9/c-compiler-handling-of-boolean-variables-290996/

+2
Apr 23 2018-11-11T00:
source share

Approaching your question in two ways:

If you are specifically talking about C ++ or any programming language that will generate assembly code, this is due to what code the compiler in ASM will generate. We are also tied to the representation of true and false in C ++. The integer should be stored in 32 bits, and I could just use a byte to store the logical expression. Asmograms for conditional statements:

For an integer:

  mov eax,dword ptr[esp] ;Store integer cmp eax,0 ;Compare to 0 je false ;If int is 0, its false ;Do what has to be done when true false: ;Do what has to be done when false 

For bool:

  mov al,1 ;Anything that is not 0 is true test al,1 ;See if first bit is fliped jz false ;Not fliped, so it false ;Do what has to be done when true false: ;Do what has to be done when false 

So why is speed comparison so dependent on compilation. In the above example, bool will be a bit quick, since cmp will mean subtraction to set flags. It also contradicts what your compiler generated.

Another approach, much simpler, is to look at the logic of the expression on it and try not to worry about how the compiler translates your code, and I think this is a much healthier way of thinking. I still believe, ultimately, that the code generated by the compiler is actually trying to give true permission. I mean, maybe, if you increase the test cases in the if statement and stick to the logical value on one side and an integer on the other, the compiler will make the generated code run faster with boolean expressions at the machine level.

I am considering this conceptual question, so I will give a conceptual answer. This discussion reminds me of the discussions that I usually talk about whether code efficiency means for smaller lines of code in an assembly. This concept seems to be universally recognized as truth. Given that tracking how fast the ALU will process each statement is not viable, the second option is to focus on the jumps and compare in the assembly. In this case, the difference between Boolean operators or integers in the code you submit becomes quite representative. The result of an expression in C ++ will return a value that will then receive a view. On the other hand, in the assembly, jumps and comparisons will be based on numerical values, regardless of what type of expression was evaluated back in your C ++ if statement. On these issues, it is important to remember that purely logical statements such as these lead to enormous computational overhead, although one bit is capable of the same thing.

0
Dec 12 '11 at 7:30
source share



All Articles