El tema anterior me hizo hacer algunos experimentos con bool
y int
en if
condiciones. Así que por curiosidad escribí 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
genera código ASM para cada función de la siguiente manera:
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
¡Sorprendentemente, g(bool)
genera más asm
instrucciones! ¿Significa que if(bool)
es un poco más lento que if(int)
? Solía pensar que bool
está especialmente diseñado para usarse en declaraciones condicionales como if
, por lo que esperaba g(bool)
generar menos instrucciones ASM, lo que las hace g(bool)
más eficientes y rápidas.
EDITAR:
No estoy usando ningún indicador de optimización a partir de ahora. Pero incluso en su ausencia, ¿por qué genera más asm? g(bool)
Es una pregunta para la que estoy buscando una respuesta razonable. También debería decirte que la -O2
bandera de optimización genera exactamente el mismo asm. Pero esa no es la cuestión. La pregunta es lo que he preguntado.
g(bool)
Es una pregunta para la que estoy buscando una respuesta razonable.Respuestas:
Tiene sentido para mi. Su compilador aparentemente define a
bool
como un valor de 8 bits, y su sistema ABI requiere que "promueva" argumentos enteros pequeños (<32 bits) a 32 bits cuando los empuja a la pila de llamadas. Entonces, para comparar abool
, el compilador genera código para aislar el byte menos significativo del argumento de 32 bits que recibe g, y lo comparacmpb
. En el primer ejemplo, elint
argumento usa los 32 bits completos que se insertaron en la pila, por lo que simplemente se compara con el conjuntocmpl
.fuente
__int64
es más rápido queint
? ¿O la CPU trata un entero de 32 bits con conjuntos de instrucciones de 32 bits por separado?Compilar con me
-03
da lo siguiente:F:
gramo:
.. por lo que compila a esencialmente el mismo código, a excepción de
cmpl
frentecmpb
. Esto significa que la diferencia, si la hay, no importa. No es justo juzgar por código no optimizado.Edite para aclarar mi punto. El código no optimizado es para depuración simple, no para velocidad. Comparar la velocidad del código no optimizado no tiene sentido.
fuente
cmpl
para uno ycmpb
para el otro?bool
es un solo byte y anint
es cuatro. No creo que haya nada más especial que eso.bool
como un tipo de 8 bits.char
, que es un byte por definición, y es la unidad direccionable más pequeña.bool
El tamaño está definido por la implementación y puede ser 1, 4 u 8, o lo que sea. Sin embargo, los compiladores tienden a convertirlo en uno.Cuando compilo esto con un conjunto sensato de opciones (específicamente -O3), esto es lo que obtengo:
Para
f()
:Para
g()
:Todavía usan instrucciones diferentes para la comparación (
cmpb
para booleano frentecmpl
a int), pero por lo demás los cuerpos son idénticos. Un vistazo rápido a los manuales de Intel me dice: ... no mucho de nada. No existe tal cosa comocmpb
nicmpl
en los manuales de Intel. Están todoscmp
y no puedo encontrar las tablas de tiempo en este momento. Sin embargo, supongo que no hay diferencia de reloj entre comparar un byte inmediato y comparar un inmediato largo, por lo que para todos los propósitos prácticos el código es idéntico.editado para agregar lo siguiente en función de su adición
La razón por la que el código es diferente en el caso no optimizado es que no está optimizado. (Sí, es circular, lo sé). Cuando el compilador recorre el AST y genera código directamente, no "sabe" nada excepto lo que está en el punto inmediato del AST en el que se encuentra. En ese momento, carece de toda la información contextual necesaria saber que en este punto específico puede tratar el tipo declarado
bool
como unint
. Obviamente, un booleano se trata de forma predeterminada como un byte y, al manipular bytes en el mundo de Intel, debe hacer cosas como firmar-extender para llevarlo a ciertos anchos para ponerlo en la pila, etc. (No puede presionar un byte .)Cuando el optimizador ve el AST y hace su magia, sin embargo, mira el contexto circundante y "sabe" cuándo puede reemplazar el código con algo más eficiente sin cambiar la semántica. Entonces "sabe" que puede usar un número entero en el parámetro y, por lo tanto, perder las conversiones y ampliaciones innecesarias.
fuente
l
yb
son sufijos utilizados únicamente en la sintaxis de AT&T. Simplemente se refieren a versiones decmp
utilizar operandos de 4 bytes (largo) y 1 byte (byte) respectivamente. Donde hay alguna ambigüedad en la sintaxis de Intel, convencionalmente el operando de memoria se etiqueta conBYTE PTR
,WORD PTR
o enDWORD PTR
lugar de poner un sufijo en el código de operación.cmp
tienen el mismo rendimiento y no hay penalizaciones de registro parcial para la lectura%dil
. (Pero eso no impide que el clang cree de manera divertida un bloqueo de registro parcial al usar el tamaño de byteand
en AL como parte del cambio de mayúsculas y minúsculas sin ramificaciones entre 99 y -99)Con GCC 4.5 en Linux y Windows al menos
sizeof(bool) == 1
,. En x86 y x86_64, no puede pasar menos que el valor de un registro de propósito general a una función (ya sea a través de la pila o un registro, según la convención de llamada, etc.).Entonces, el código para bool, cuando no está optimizado, en realidad llega hasta cierto punto para extraer ese valor bool de la pila de argumentos (usando otra ranura de pila para guardar ese byte). Es más complicado que simplemente extraer una variable nativa del tamaño de un registro.
fuente
sizeof(bool)
ysizeof(wchar_t)
están definidos por la implementación " . Por lo tanto, decirlosizeof(bool) == 1
no es estrictamente correcto a menos que esté hablando de una versión específica de un compilador específico.A nivel de máquina no existe tal cosa como bool
Muy pocas arquitecturas de conjuntos de instrucciones definen algún tipo de operando booleano, aunque a menudo hay instrucciones que desencadenan una acción en valores distintos de cero. Para la CPU, por lo general, todo es uno de los tipos escalares o una cadena de ellos.
Un compilador y una ABI determinados deberán elegir tamaños específicos para
int
ybool
cuando, como en su caso, estos sean tamaños diferentes, pueden generar código ligeramente diferente y, en algunos niveles de optimización, uno puede ser un poco más rápido.¿Por qué bool es de un byte en muchos sistemas?
Es más seguro elegir un
char
tipo para bool porque alguien podría hacer una gran variedad de ellos.Actualización: por "más seguro", quiero decir: para el compilador y los implementadores de la biblioteca. No estoy diciendo que las personas necesiten volver a implementar el tipo de sistema.
fuente
bool
estuviera representada por bits; por lo que el byte será una buena compensación por la compacidad de velocidad / datos en muchas implementaciones.char
lugar debool
", sino que simplemente usó "char
tipo" para significar "1 byte" cuando se refería al tamaño que el compilador elige para losbool
objetos.Sí, la discusión es divertida. Pero solo pruébalo:
Código de prueba:
Compilado en una computadora portátil Ubuntu 10.10 de 64 bits con: g ++ -O3 -o / tmp / test_i /tmp/test_i.cpp
Comparación basada en enteros:
Prueba booleana / impresión sin comentar (y entero comentado):
Son iguales con 1 asignación y 2 comparaciones en cada bucle de más de 30 millones de bucles. Encuentre algo más para optimizar. Por ejemplo, no use strcmp innecesariamente. ;)
fuente
Dependerá principalmente del compilador y la optimización. Hay una discusión interesante (independiente del lenguaje) aquí:
¿"If ([bool] == true)" requiere un paso más que "if ([bool])"?
Además, eche un vistazo a esta publicación: http://www.linuxquestions.org/questions/programming-9/c-compiler-handling-of-boolean-variables-290996/
fuente
Abordar su pregunta de dos maneras diferentes:
Si está hablando específicamente de C ++ o de cualquier lenguaje de programación que produzca código ensamblador, estamos sujetos a qué código generará el compilador en ASM. También estamos vinculados a la representación de verdadero y falso en c ++. Un número entero tendrá que almacenarse en 32 bits, y podría simplemente usar un byte para almacenar la expresión booleana. Fragmentos de código asm para declaraciones condicionales:
Para el entero:
Para el bool:
Entonces, es por eso que la comparación de velocidad depende tanto de la compilación. En el caso anterior, el bool sería un poco rápido ya
cmp
que implicaría una resta para poner las banderas. También contradice lo que generó su compilador.Otro enfoque, mucho más simple, es mirar la lógica de la expresión por sí misma y tratar de no preocuparse por cómo el compilador traducirá su código, y creo que esta es una forma de pensar mucho más saludable. Sigo creyendo, en última instancia, que el código generado por el compilador está tratando de dar una resolución veraz. Lo que quiero decir es que, tal vez si aumenta los casos de prueba en la instrucción if y se queda con booleano en un lado y entero en otro, el compilador hará que el código generado se ejecute más rápido con expresiones booleanas en el nivel de la máquina.
Estoy considerando que esta es una pregunta conceptual, así que daré una respuesta conceptual. Esta discusión me recuerda las discusiones que tengo comúnmente sobre si la eficiencia del código se traduce o no en menos líneas de código en ensamblador. Parece que este concepto es generalmente aceptado como cierto. Teniendo en cuenta que no es viable realizar un seguimiento de la rapidez con que la ALU manejará cada declaración, la segunda opción sería centrarse en los saltos y las comparaciones en el ensamblaje. Cuando ese es el caso, la distinción entre declaraciones booleanas o enteros en el código que presentó se vuelve bastante representativa. El resultado de una expresión en C ++ devolverá un valor que luego se le dará una representación. En montaje, por otro lado, los saltos y las comparaciones se basarán en valores numéricos independientemente del tipo de expresión que se esté evaluando en su declaración if de C ++. Es importante en estas preguntas recordar que afirmaciones puramente lógicas como estas terminan con una enorme sobrecarga computacional, aunque un solo bit sería capaz de hacer lo mismo.
fuente