Naw*_*waz 90 c++ int assembly boolean
上面的话题让我做一些实验与bool和int的if状态.所以出于好奇我写了这个程序:
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(){}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)asm代码 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
Run Code Online (Sandbox Code Playgroud)令人惊讶的是,g(bool)生成更多asm指令!这是否意味着if(bool)比它慢一点if(int)?我曾经认为bool特别设计用于条件语句,例如if,因此我期望g(bool)生成更少的asm指令,从而提高g(bool)效率和速度.
编辑:
我现在没有使用任何优化标志.但即使没有它,为什么它产生更多的asm g(bool)是一个问题,我正在寻找一个合理的答案.我还应该告诉你,-O2优化标志生成完全相同的asm.但这不是问题.问题是我问的问题.
She*_*ley 98
我感觉合理.您的编译器显然将a定义bool为8位值,并且系统ABI要求它在将小(<32位)整数参数推送到调用堆栈时将其"提升"为32位.因此,为了比较a bool,编译器生成代码以隔离g接收的32位参数的最低有效字节,并将其与之进行比较cmpb.在第一个例子中,int参数使用被压入堆栈的完整32位,因此它只是与整个事物进行比较cmpl.
Ale*_*ler 78
编译-03为我提供以下内容:
F:
pushl %ebp
movl %esp, %ebp
cmpl $1, 8(%ebp)
popl %ebp
sbbl %eax, %eax
andb $58, %al
addl $99, %eax
ret
Run Code Online (Sandbox Code Playgroud)
G:
pushl %ebp
movl %esp, %ebp
cmpb $1, 8(%ebp)
popl %ebp
sbbl %eax, %eax
andb $58, %al
addl $99, %eax
ret
Run Code Online (Sandbox Code Playgroud)
..所以它编译成基本上是相同的代码,除了cmplVS cmpb.这意味着差异(如果有的话)无关紧要.从未经优化的代码判断是不公平的.
编辑以澄清我的观点.未优化的代码用于简单的调试,而不是速度.比较未经优化的代码的速度是毫无意义的.
JUS*_*ION 26
当我使用一组理智的选项(特别是-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
Run Code Online (Sandbox Code Playgroud)
用于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
Run Code Online (Sandbox Code Playgroud)
它们仍然使用不同的指令进行比较(cmpb对于boolean与cmplint),但是其他主体是相同的.快速浏览一下英特尔手册告诉我:......没什么特别的.有因为没有这样的事情cmpb还是cmpl在英特尔手册.他们都是cmp,我现在找不到时间表.然而,我猜测,比较一个立即字节与比较一个长立即数之间没有时钟差异,所以对于所有实际目的,代码是相同的.
编辑以根据您的添加添加以下内容
代码在未经优化的情况下不同的原因是它未被优化.(是的,它是循环的,我知道.)当编译器遍历AST并直接生成代码时,它不会"知道"除了它所处的AST的直接点之外的任何内容.此时它缺少所需的所有上下文信息.要知道在这个特定点上它可以将声明的类型bool视为一个int.布尔值显然默认被视为一个字节,当在英特尔世界中操作字节时,你必须做一些事情,比如sign-extend将它带到一定的宽度以将它放在堆栈上等等.(你不能推送一个字节.)
然而,当优化器查看AST并发挥其魔力时,它会查看周围的上下文并"知道"何时可以在不改变语义的情况下用更高效的代码替换代码.因此它"知道"它可以在参数中使用整数,从而失去不必要的转换和扩展.
Mat*_*Mat 13
至少在Linux和Windows上使用GCC 4.5 sizeof(bool) == 1.在x86和x86_64上,您不能将少于通用寄存器的值传递给函数(无论是通过堆栈还是寄存器,取决于调用约定等...).
所以bool的代码,当未经优化时,实际上会花一些时间从参数堆栈中提取bool值(使用另一个堆栈槽来保存该字节).它比仅提取本地寄存器大小的变量更复杂.
很少有指令集体系结构定义任何类型的布尔操作数类型,尽管通常有指令触发对非零值的操作.通常,对于CPU来说,一切都是标量类型之一或其中的一串.
给定的编译器和给定的ABI将需要选择特定的大小int,bool以及何时,在您的情况下,这些是不同的大小,它们可能会生成稍微不同的代码,并且在某些优化级别,可能会稍快一些.
char为bool 选择一个类型会更安全,因为有人可能会制作一个非常大的类型.
更新:通过"更安全",我的意思是:对于编译器和库实现者.我不是说人们需要重新实现系统类型.
是的,讨论很有趣.但只是测试一下:
测试代码:
#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;
}
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
布尔测试/打印未注释(和整数注释):
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
Run Code Online (Sandbox Code Playgroud)
它们与1个赋值相同,2个比较每个循环超过3000万个循环.找到其他优化的东西.例如,不要不必要地使用strcmp.;)