哪个更快:if(bool)或if(int)?
哪个值更好用? 布尔真或整数1?
上面的话题让我在if
条件下用bool
和int
做了一些实验。 所以出于好奇,我写了这个程序:
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
为每个函数生成asm代码,如下所示:
-
asm代码
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
-
g(bool)
asm代码__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
令人惊讶的是, g(bool)
生成更多的asm
指令! 这是否意味着if(bool)
比if(int)
慢一点? 我曾经认为bool
是特别devise用于条件语句如if
,所以我期待g(bool)
产生较less的asm指令,从而使g(bool)
更有效和快速。
编辑:
截至目前,我没有使用任何优化标志。 但即使没有它,为什么它会为g(bool)
生成更多的语句,我正在寻找一个合理的答案。 我也应该告诉你-O2
优化标志产生完全相同的asm。 但这不是问题。 问题是我问过的。
我感觉合理。 您的编译器显然将bool
定义为8位值,而您的系统ABI在将它们推送到调用堆栈时要求它将“小”(<32位)整数参数“提升”为32位。 所以为了比较一个bool
,编译器生成代码来隔离g接收的32位参数的最低有效字节,并将其与cmpb
进行比较。 在第一个例子中, int
参数使用被压入堆栈的完整32位,所以它只是与cmpl
进行比较。
用-03
编译给我以下内容:
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
..所以它编译本质上相同的代码,除了cmpl
vs cmpb
。 这意味着,如果有的话,这个差别并不重要。 通过未经优化的代码来判断是不公平的。
编辑澄清我的观点。 未优化的代码是为了简单的debugging,而不是速度。 比较未优化代码的速度是没有意义的。
当我用一套理智的选项(特别是-O3)进行编译时,这里是我得到的:
对于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
对于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
他们仍然使用不同的指令进行比较( cmpb
为boolean而cmpl
为int),否则它们是相同的。 英特尔手册快速浏览告诉我:…什么都没有。 Intel手册中没有cmpb
或cmpl
这样的东西。 他们都是cmp
,我目前找不到时间表。 不过,我猜测,即时比较一个字节与比较一个长立即数之间没有时钟差异,因此对于所有实际的目的来说,代码是相同的。
编辑添加以下基于您的添加
代码在未优化的情况下是不同的,因为它没有被优化。 (是的,这是循环的,我知道。)当编译器直接运行AST并直接生成代码时,它除了在AST的即时位置之外不知道任何东西。在这一点上它缺less所需的所有上下文信息要知道在这个特定的点上它可以将声明的typesbool
作为int
。 一个布尔值显然默认为一个字节,当在英特尔世界里操纵字节时,你必须做一些像sign-extend这样的事情来使它达到一定的宽度,以便把它放在堆栈上等等。(你不能把一个字节。)
然而,当优化器查看AST并且发挥它的魔力时,它会查看周围的上下文并“知道”何时可以用更有效的方式replace代码而不改变语义。 所以它“知道”它可以在参数中使用整数,从而减less不必要的转换和扩大。
至less在Linux和Windows上使用GCC 4.5, sizeof(bool) == 1
。 在x86和x86_64上,不能将通用寄存器的值传递给函数(不pipe是通过栈还是寄存器,这取决于调用约定等)。
所以当未优化时,bool的代码实际上需要一定的时间来从参数堆栈中提取这个bool值(使用另一个堆栈槽来保存该字节)。 这不仅仅是拉本地寄存器大小的variables更复杂。
在机器层面上,没有像布尔这样的东西
很less有指令集体系结构定义任何types的布尔操作数types,尽pipe经常有指令触发对非零值的操作。 对于CPU来说,通常情况下,所有东西都是标量types之一或者是其中的一串。
一个给定的编译器和一个给定的ABI将需要为int
和bool
select特定的大小,并且在你的情况下,如果这些大小不同,它们可能会生成稍微不同的代码,并且在某些优化级别可能会稍微快一些。
为什么在许多系统上布尔单字节?
为boolselect一个char
types是比较安全的,因为有些人可能会创build大量的char
types。
更新:由“更安全”,我的意思是: 对于编译器和库实现者。 我并不是说人们需要重新实现系统types。
是的,讨论的乐趣。 但只是testing它:
testing代码:
#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; }
在64位Ubuntu 10.10笔记本电脑上编译:g ++ -O3 -o / tmp / test_i /tmp/test_i.cpp
整数比较:
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
布尔testing/打印取消注释(和整数注释):
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
它们与1个赋值和2个比较相同,每个循环超过3000万个循环。 find其他的东西来优化。 例如,不要不必要地使用strcmp。 ;)
它将主要取决于编译器和优化。 这里有一个有趣的讨论(语言不可知论者):
“if([bool] == true)”是否比“if([bool])”多一步?
另外,看看这个职位: http : //www.linuxquestions.org/questions/programming-9/c-compiler-handling-of-boolean-variables-290996/
以两种不同的方式接近你的问题:
如果您正在专门讨论C ++或任何将为此产生汇编代码的编程语言,那么我们就会被绑定到编译器将在ASM中生成的代码。 我们还必须在c ++中表示真假。 一个整数将被存储在32位,我可以简单地使用一个字节来存储布尔expression式。 Asm片断条件语句:
对于整数:
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
对于布尔:
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's false ;Do what has to be done when true false: ;Do what has to be done when false
所以,这就是速度比较如此依赖于编译的原因。 在上面的例子中,bool会稍微快一点,因为cmp
意味着设置标志的减法。 它也与你的编译器生成的东西相矛盾。
另一种更简单的方法是单独看expression式的逻辑,不要担心编译器如何翻译你的代码,我认为这是一种更健康的思维方式。 我仍然相信,编译器生成的代码实际上是在试图给出一个真实的解决scheme。 我的意思是说,也许如果你在if语句中增加testing用例,并且在一边是布尔值而另一边是整数,那么编译器会这样做,生成的代码将在机器级别用布尔expression式执行得更快。
我正在考虑这是一个概念性的问题,所以我会给出一个概念性的答案。 这个讨论让我想起了我通常关于代码效率是否代表组装中较less代码行的讨论。 这个概念似乎被普遍接受为是真的。 考虑到跟踪ALU处理每条语句的速度是不可行的,第二个选项是集中跳转和比较汇编。 在这种情况下,你所提供的代码中的布尔语句或整数之间的区别就变得相当有代表性。 在C ++中的expression式的结果将返回一个值,然后将给出一个表示。 另一方面,在汇编中,跳转和比较将以数字值为基础,而不pipe在C ++语句中如何评估expression式的types。 在这些问题上,重要的是要记住,纯粹的逻辑语句如此,最终会带来巨大的计算开销,尽pipe一个单一的位就可以实现相同的function。