C++ 哪个更快:if(bool)还是if(int)?
上面的主题让我在C++ 哪个更快:if(bool)还是if(int)?,c++,assembly,int,boolean,C++,Assembly,Int,Boolean,上面的主题让我在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代码,如下所示: f(in
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代码,如下所示:
f(int)
g(bool)
g(bool)
生成了更多的asm
指令!这是否意味着if(bool)
比if(int)
慢一点?我曾经认为bool
是专门设计用来在条件语句中使用的,比如if
,所以我希望g(bool)
生成更少的asm指令,从而使g(bool)
更加高效和快速
编辑:
我现在没有使用任何优化标志。但是,即使没有它,为什么它会为g(bool)
生成更多的asm,这是一个我正在寻找合理答案的问题。我还应该告诉您,-O2
优化标志生成完全相同的asm。但这不是问题所在。这个问题就是我问的
使用
-03
编译为我提供了以下信息:
f:
g:
pushl %ebp
movl %esp, %ebp
cmpb $1, 8(%ebp)
popl %ebp
sbbl %eax, %eax
andb $58, %al
addl $99, %eax
ret
。。因此它编译成基本相同的代码,除了cmpl
vscmpb
。
这意味着差异,如果有的话,并不重要。根据未优化的代码进行判断是不公平的
编辑以澄清我的观点。未优化的代码用于简单调试,而不是速度。比较未优化代码的速度毫无意义。至少在Linux和Windows上使用GCC4.5,
sizeof(bool)==1
。在x86和x86_64上,不能将少于通用寄存器的值传递给函数(无论是通过堆栈还是寄存器,取决于调用约定等)
因此,bool的代码在未优化时,实际上会经过一定的长度从参数堆栈中提取bool值(使用另一个堆栈槽保存该字节)。这比仅仅提取一个本机寄存器大小的变量要复杂得多。对我来说很有意义。编译器显然将
bool
定义为8位值,系统ABI要求它在将小(<32位)整型参数推送到调用堆栈时将其“升级”为32位。因此,为了比较bool
,编译器生成代码来隔离g接收的32位参数的最低有效字节,并将其与cmpb
进行比较。在第一个示例中,int
参数使用推送到堆栈上的全部32位,因此它只是与cmpl
进行比较,当我使用一组合理的选项(特别是-O3)编译时,我得到了以下结果:
对于f()
:
对于g()
:
它们仍然使用不同的指令进行比较(cmpb
表示布尔值,而cmpl
表示int),但在其他方面,主体是相同的。快速浏览一下英特尔手册,我会发现:。。。什么都没有。英特尔手册中没有cmpb
或cmpl
。它们都是cmp,我现在找不到计时表。然而,我猜测,比较字节立即数和比较长立即数之间没有时钟差异,因此对于所有实际目的,代码都是相同的
编辑以根据您的添加添加以下内容 在未优化的情况下,代码不同的原因是它未优化。(是的,它是循环的,我知道。)当编译器遍历AST并直接生成代码时,除了它所在的AST的直接点之外,它什么都不“知道”。在这一点上,它缺乏所有需要了解的上下文信息,在这个特定点上,它可以将声明的类型
bool
视为int
。显然,布尔值在默认情况下被视为字节,在英特尔世界中处理字节时,必须执行诸如“符号扩展”之类的操作,使其达到一定的宽度,以便将其放入堆栈中,等等(不能推送字节)
然而,当优化器查看AST并发挥其神奇作用时,它会查看周围的上下文并“知道”何时可以在不改变语义的情况下用更高效的代码替换代码。因此它“知道”它可以在参数中使用整数,从而丢失不必要的转换和加宽。它将主要取决于编译器和优化。这里有一个有趣的讨论(语言不可知论): 另外,看看这篇文章:在机器级别上没有bool这样的东西 很少有指令集体系结构定义任何类型的布尔操作数类型,尽管经常有指令触发对非零值的操作。对于CPU来说,通常,一切都是标量类型或标量类型的一个字符串 一个给定的编译器和一个给定的ABI需要为
int
和bool
选择特定的大小,当这些大小不同时,它们可能会生成稍有不同的代码,并且在某些优化级别上可能会稍快一些
为什么bool在许多系统上是一个字节?
为bool选择一个char
类型更安全,因为有人可能会创建一个非常大的数组
更新:所谓“更安全”,我的意思是:对于编译器和库实现者。我不是说人们需要重新实现系统类型。是的,讨论很有趣。但只要测试一下: 测试代码:
#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;
}
布尔测试/打印未注释(和整数注释):
在3000万个循环中,每个循环都有1个赋值和2个比较。找到其他要优化的内容。例如,不要不必要地使用strcmp 以两种不同的方式处理您的问题:
如果你是专门谈论C++的
pushl %ebp
movl %esp, %ebp
cmpl $1, 8(%ebp)
popl %ebp
sbbl %eax, %eax
andb $58, %al
addl $99, %eax
ret
pushl %ebp
movl %esp, %ebp
cmpb $1, 8(%ebp)
popl %ebp
sbbl %eax, %eax
andb $58, %al
addl $99, %eax
ret
.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
.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
#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;
}
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
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
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