O tópico acima me fez fazer alguns experimentos com bool
e int
em if
condições. Então, apenas por curiosidade, escrevi este programa:
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
gera código ASM para cada função da seguinte maneira:
código ASM para
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
código ASM para
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
Surpreendentemente, g(bool)
gera mais asm
instruções! Isso significa que if(bool)
é um pouco mais lento do que if(int)
? Eu costumava pensar que bool
era especialmente projetado para ser usado em instruções condicionais como if
, portanto, esperava g(bool)
gerar menos instruções asm, tornando-as g(bool)
mais eficientes e rápidas.
EDITAR:
Não estou usando nenhum sinalizador de otimização no momento. Mas mesmo a ausência dela, por que gera mais g(bool)
questionamento? É uma questão para a qual estou procurando uma resposta razoável. Devo também dizer que o -O2
sinalizador de otimização gera exatamente o mesmo asm. Mas essa não é a questão. A questão é o que eu perguntei.
g(bool)
questionamento? É uma questão para a qual procuro uma resposta razoável.Respostas:
Faz sentido para mim. Aparentemente, seu compilador define a
bool
como um valor de 8 bits e o ABI do sistema exige que ele "promova" argumentos inteiros pequenos (<32 bits) para 32 bits ao colocá-los na pilha de chamadas. Portanto, para comparar abool
, o compilador gera código para isolar o byte menos significativo do argumento de 32 bits que g recebe e o comparacmpb
. No primeiro exemplo, oint
argumento usa os 32 bits completos que foram colocados na pilha, então ele simplesmente compara com o todo comcmpl
.fonte
__int64
são mais rápidos do queint
? Ou a CPU lida com inteiros de 32 bits com conjuntos de instruções de 32 bits separadamente?Compilar com
-03
oferece o seguinte para mim:f:
g:
.. por isso compila para essencialmente o mesmo código, exceto para
cmpl
vscmpb
. Isso significa que a diferença, se houver, não importa. Julgar por código não otimizado não é justo.Edite para esclarecer meu ponto. O código não otimizado é para depuração simples, não para velocidade. Comparar a velocidade de um código não otimizado não faz sentido.
fonte
cmpl
para um ecmpb
para o outro?bool
é um único byte e anint
é quatro. Não acho que haja nada mais especial do que isso.bool
como um tipo de 8 bits.char
, que é um byte por definição e é a menor unidade endereçável.bool
O tamanho de é definido pela implementação e pode ser 1, 4 ou 8 ou qualquer outro. No entanto, os compiladores tendem a torná-lo um.Quando eu compilo isso com um conjunto razoável de opções (especificamente -O3), eis o que obtenho:
Para
f()
:Para
g()
:Eles ainda usam instruções diferentes para a comparação (
cmpb
para booleano vs.cmpl
para int), mas fora isso os corpos são idênticos. Uma rápida olhada nos manuais da Intel me diz: ... quase nada. Não existe tal coisa comocmpb
oucmpl
nos manuais da Intel. Eles são todoscmp
e não consigo encontrar as tabelas de tempo no momento. Estou supondo, entretanto, que não há diferença de clock entre comparar um imediato byte e um imediato longo, portanto, para todos os fins práticos, o código é idêntico.editado para adicionar o seguinte com base na sua adição
A razão pela qual o código é diferente no caso não otimizado é que ele não é otimizado. (Sim, é circular, eu sei.) Quando o compilador percorre o AST e gera o código diretamente, ele não "sabe" nada, exceto o que está no ponto imediato do AST em que está. Nesse ponto, faltam todas as informações contextuais necessárias saber que, neste ponto específico, ele pode tratar o tipo declarado
bool
como umint
. Um booleano é obviamente tratado por padrão como um byte e ao manipular bytes no mundo da Intel você tem que fazer coisas como estender o sinal para trazê-lo a certas larguras para colocá-lo na pilha, etc. (Você não pode empurrar um byte .)Quando o otimizador visualiza o AST e faz sua mágica, entretanto, ele olha para o contexto circundante e "sabe" quando pode substituir o código por algo mais eficiente sem alterar a semântica. Portanto, ele "sabe" que pode usar um número inteiro no parâmetro e, assim, perder as conversões e ampliações desnecessárias.
fonte
l
eb
são sufixos usados apenas na sintaxe da AT&T. Eles apenas se referem a versões decmp
operandos de 4 bytes (longo) e 1 byte (byte), respectivamente. Onde houver qualquer ambigüidade na sintaxe intel, convencionalmente o operando de memória é marcado comBYTE PTR
,WORD PTR
ou emDWORD PTR
vez de colocar um sufixo no opcode.cmp
têm o mesmo desempenho e não há penalidades de registro parcial para leitura%dil
. (Mas isso não impede o clang de criar divertidamente uma paralisação de registro parcial usando o tamanho do byteand
em AL como parte da inversão de caixa sem ramificação entre 99 e -99.)Com GCC 4.5 em Linux e Windows, pelo menos
sizeof(bool) == 1
,. No x86 e no x86_64, você não pode passar menos do que o valor de um registro de uso geral para uma função (seja por meio da pilha ou de um registro, dependendo da convenção de chamada, etc ...).Portanto, o código para bool, quando não otimizado, vai até certo ponto para extrair esse valor bool da pilha de argumentos (usando outro slot de pilha para salvar esse byte). É mais complicado do que apenas puxar uma variável de tamanho de registro nativo.
fonte
sizeof(bool)
esizeof(wchar_t)
são definidos pela implementação. ” Portanto, dizersizeof(bool) == 1
não é estritamente correto, a menos que você esteja falando sobre uma versão específica de um compilador específico.No nível da máquina, não existe bool
Muito poucas arquiteturas de conjunto de instruções definem qualquer tipo de operando booleano, embora haja frequentemente instruções que disparam uma ação em valores diferentes de zero. Para a CPU, geralmente, tudo é um dos tipos escalares ou uma string deles.
Um determinado compilador e uma determinada ABI precisarão escolher tamanhos específicos para
int
ebool
e quando, como no seu caso, esses tamanhos forem diferentes, eles podem gerar códigos ligeiramente diferentes e, em alguns níveis de otimização, um pode ser um pouco mais rápido.Por que o bool tem um byte em muitos sistemas?
É mais seguro escolher um
char
tipo de bool porque alguém pode fazer uma grande variedade deles.Atualização: por "mais seguro", quero dizer: para o compilador e implementadores de biblioteca. Não estou dizendo que as pessoas precisam reimplementar o tipo de sistema.
fonte
bool
fossem representados por bits; então, byte será uma boa troca para velocidade / compactação de dados em muitas implementações.char
vez debool
", mas simplesmente usou "char
tipo" para significar "1 byte" ao se referir ao tamanho que o compilador escolhe para osbool
objetos.Sim, a discussão é divertida. Mas apenas teste:
Código de teste:
Compilado em um laptop Ubuntu 10.10 de 64 bits com: g ++ -O3 -o / tmp / test_i /tmp/test_i.cpp
Comparação baseada em inteiros:
Teste / impressão booleano sem comentário (e inteiro comentado):
Eles são iguais com 1 atribuição e 2 comparações, cada loop em 30 milhões de loops. Encontre algo mais para otimizar. Por exemplo, não use strcmp desnecessariamente. ;)
fonte
Dependerá principalmente do compilador e da otimização. Há uma discussão interessante (agnóstica de idioma) aqui:
"If ([bool] == true)" requer uma etapa a mais do que "if ([bool])"?
Além disso, dê uma olhada neste post: http://www.linuxquestions.org/questions/programming-9/c-compiler-handling-of-boolean-variables-290996/
fonte
Abordando sua pergunta de duas maneiras diferentes:
Se você está falando especificamente sobre C ++ ou qualquer linguagem de programação que irá produzir código assembly para esse assunto, estamos limitados a qual código o compilador irá gerar no ASM. Também estamos limitados à representação de verdadeiro e falso em c ++. Um inteiro terá que ser armazenado em 32 bits e eu poderia simplesmente usar um byte para armazenar a expressão booleana. Snippets de Asm para declarações condicionais:
Para o inteiro:
Para o bool:
Então, é por isso que a comparação de velocidade é tão dependente da compilação. No caso acima, o bool seria um pouco rápido, pois
cmp
implicaria em uma subtração para definir os sinalizadores. Também contradiz o que o seu compilador gerou.Outra abordagem, muito mais simples, é olhar para a lógica da expressão por conta própria e tentar não se preocupar em como o compilador traduzirá seu código, e acho que essa é uma maneira muito mais saudável de pensar. Ainda acredito, em última análise, que o código gerado pelo compilador está, na verdade, tentando fornecer uma resolução verdadeira. O que quero dizer é que, talvez se você aumentar os casos de teste na instrução if e ficar com booleano em um lado e inteiro no outro, o compilador fará com que o código gerado seja executado mais rápido com expressões booleanas no nível da máquina.
Estou considerando que esta é uma questão conceitual, então darei uma resposta conceitual. Essa discussão me lembra das discussões que normalmente tenho sobre se a eficiência do código se traduz ou não em menos linhas de código em assembly. Parece que esse conceito é geralmente aceito como verdadeiro. Considerando que não é viável manter o controle da rapidez com que a ALU irá lidar com cada instrução, a segunda opção seria focar nos saltos e comparações na montagem. Quando for esse o caso, a distinção entre declarações booleanas ou inteiros no código que você apresentou torna-se bastante representativa. O resultado de uma expressão em C ++ retornará um valor que receberá uma representação. Na montagem, por outro lado, os saltos e comparações serão baseados em valores numéricos, independentemente do tipo de expressão que está sendo avaliada em sua instrução C ++ if. É importante, nessas questões, lembrar que declarações puramente lógicas como essas acabam com uma grande sobrecarga computacional, embora um único bit seja capaz da mesma coisa.
fonte