Que el valor es mejor utilizar? Boolean true o Entero 1?

El tema anterior me hizo hacer algunos experimentos con bool y int en if condición. Tan sólo por curiosidad que 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 en ensamblador para cada una de las funciones de la siguiente manera:

  • código en 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 en 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! Qué significa que if(bool) es poco más lento que if(int)? Yo solía pensar bool está especialmente diseñado para ser utilizado en la instrucción condicional como if, así que esperaba g(bool) para generar menos asm instrucciones, haciendo g(bool) más eficiente y rápido.

EDICIÓN:

Que no voy a usar ninguna optimización de la bandera como la de ahora. Pero incluso en ausencia de ella, ¿por qué generar más asm para g(bool) es una pregunta para la que yo estoy buscando una respuesta razonable. También debo decirte que -O2 la optimización de la bandera genera exactamente el mismo asm. Pero esa no es la cuestión. La pregunta es lo que le he pedido.


  • dependerá si cmpb es más rápido que cmpl
  • Es también una injusta prueba a menos que se les compare con razonable optimizaciones habilitadas.
  • Yo no estoy usando ninguna optimización de banderas con cualquiera de ellos. Pero incluso en ausencia de ella, ¿por qué genera más asm para g(bool) es una pregunta para la que yo estoy buscando una respuesta razonable.
  • Si hay algún código real en las llaves después de la sentencia if, entonces … la respuesta es…. son los mismos.
  • Le sugiero que utilice -O3, pero asegúrese de que el compilador no sólo en línea y negar su código porque no tiene ningún efecto en el momento. 😛
  • Yo también creo que depende de la arquitectura y el compilador
  • Ver la edición 😀
  • Ese es el problema. Una versión de mayo, por extraña razón, generar código que lleva un par más de instrucciones cuando se compila sin optimizaciones, pero una mejor medida de la velocidad intrínseca de las pruebas de tipo int o bool sería comparar ambos compilado con-O. Sin optimizaciones, eres sólo la prueba de que uno es más traducido directamente en el compilador de la representación intermedia(s).
  • También, es mover el bool en el %eax% registrarse porque no es el tamaño de palabra? En comparación a int, que es el tamaño de palabra, por tanto, que es uno menos de optimización; lo que sin duda se habrían hecho si la optimización de los pisos estaban habilitados.
  • ¿Por qué ir a la dificultad de la lectura de la asm, pero no sólo en la ejecución del programa y el momento el resultado? El número de instructiosn realidad no dicen mucho sobre el rendimiento. Que deben tener en cuenta no sólo la instrucción de longitudes, pero también de las dependencias y de los tipos de instrucciones (algunas de ellas son decodificados utilizando el más lento del camino, que unidades de ejecución que requieren, lo que es la latencia y el rendimiento de la instrucción, es una rama? Un memmory de acceso?
  • ¿por qué genera más asm para g(bool) es una pregunta para la que yo estoy buscando una respuesta razonable» Que no es lo que dice el título.
  • a) Para cualquier trabajo razonable el código nunca va a hacer, la diferencia de tiempo entre si (bool) o si (int) debería ser casi irrelevante. Escribir código legible. b) Escribir un entorno de pruebas, en donde usted sólo tiene que pasar en 2 alternativas de una función, la cual proporciona datos empíricos de 1 a 10 programa se ejecuta con 1000 a 1000000000 casos con diferentes configuraciones de compilador y compiladores diferentes – por la fabricación y versión – y mira a tu micro puntos de referencia a sí mismo.
  • desconocido,y @Malvolio: Que es, obviamente, yo no estoy haciendo todos estos para el código de producción. Como ya he mencionado al principio de mi post que «Tan sólo por curiosidad que escribí este programa». Así que sí, es puramente hipotética uno.
  • Es una pregunta legítima. Son, ya sea equivalente o uno es más rápido. El ASM probablemente fue publicado en un intento de ser útil o pensar en voz alta, así que en lugar de usarlo como una manera de esquivar la pregunta y decir «simplemente escribir código legible», sólo tienes que responder a la pregunta o STFU si usted no sabe o no tiene nada útil que decir 😉 Mi aportación es que la pregunta es responder, y «sólo escribir código legible» no es sino una esquivando la pregunta.

InformationsquelleAutor Nawaz | 2011-04-23

8 Comentarios

  1. 98

    Tiene sentido para mí. Su compilador aparentemente define un bool como un valor de 8 bits, y el sistema ABI requiere la «promoción» de pequeño (< 32-bit) argumentos enteros de 32 bits cuando se empuja a la pila de llamadas. Así que para comparar un bool, el compilador genera código para aislar el byte menos significativo de la de 32 bits argumento de que g recibe, y la compara con cmpb. En el primer ejemplo, el int argumento utiliza el de 32 bits que se inserta en la pila, por lo que simplemente se compara con la totalidad de la cosa con cmpl.

    • Este es un buen intento de responder a la pregunta. +1
    • Estoy de acuerdo. Esto ayuda a iluminar que cuando la elección de un tipo de variable, usted está eligiendo para dos potencialmente competidoras a los efectos de, espacio de almacenamiento vs rendimiento computacional.
    • ¿Esto también se aplica a los procesos de 64 bits, que __int64 es más rápido que int? O CPU ofertas entero de 32 bits con 32-bit conjuntos de instrucciones por separado?
    • tal vez vale la pena rodar otra pregunta?
  2. 78

    Compilar con -03 da el siguiente para mí:

    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

    .. por lo que se compila a esencialmente el mismo código, excepto para cmpl vs cmpb.
    Esto significa que la diferencia, si la hay, no importa. A juzgar por unoptimized código no es justo.


    Editar para aclarar mi punto. Unoptimized código es para la depuración simple, no por la velocidad. Comparando la velocidad de unoptimized código es absurdo.

    • -O2 es más que suficiente, ya que se genera el mismo asm (los que he mencionado en la EDICIÓN).
    • Como mucho estoy de acuerdo con tu conclusión, creo que estás omitiendo la parte interesante. Porque el uso de cmpl para uno y cmpb para el otro?
    • Porque un bool es de un solo byte y un int es de cuatro. Creo que no hay nada más especial que eso.
    • Creo que otras respuestas prestado más atención a las razones: es porque la plataforma en cuestión trata bool como de 8 bits tipo.
    • a la derecha, pero la omi que pertenece en la respuesta. Realmente no se puede conclued que «no hay diferencia» si usted encuentra una diferencia y no se menciona cuáles son las implicaciones. 🙂
    • Bailey: Una bool es una sola poco, no de byte.
    • No. C++ no tiene tipos de datos bit. El tipo más pequeño es char, que es un byte por definición, y es la más pequeña unidad direccionable. bool‘s es el tamaño de la aplicación definida, y puede ser de 1, 4, o 8, o lo que sea. Los compiladores tienden a hacer uno, aunque.
    • Lo siento, pensando en Java.
    • Bueno, eso es difícil en Java, también. Java dice que los datos booleano que representa es el valor de un bit, pero la manera en que poco se almacena todavía está definido por la implementación. Pragmática equipos simplemente no los bits de la dirección.
    • Pensé -g fue para la depuración simple.
    • -g es realmente bueno para que gcc emiten información de depuración, pero se vuelve más y más inútil como el optimizador de nivel se incrementa. El manual de gcc pone muy bien: GCC allows you to use -g with -O. The shortcuts taken by optimized code may occasionally produce surprising results.

  3. 26

    Cuando me reúna con un buen conjunto de opciones (en concreto -O3), aquí es lo que me pasa:

    Para 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

    Para 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

    Aún se usan diferentes instrucciones para la comparación (cmpb booleana vs cmpl para int), pero de lo contrario, los cuerpos son idénticos. Un rápido vistazo a los manuales de Intel me dice: … no se mucho de nada. No hay tal cosa como cmpb o cmpl en el Intel manuales. Son todos cmp y no puedo encontrar el calendario de las tablas en el momento. Supongo, sin embargo, que no hay reloj diferencia entre la comparación de un byte inmediata y comparar un largo inmediata, así que para todos los efectos prácticos, el código es idéntico.


    editado para añadir el siguiente según su adición

    La razón de que el código es diferente en el unoptimized caso es que es unoptimized. (Sí, es circular, lo sé.) Cuando el compilador se pasea por la AST y genera el código directamente, no «sabe» nada, excepto lo que está en el punto inmediato de la AST en el que esta. En ese momento, se carece de toda información contextual necesaria para saber que en este punto específico se puede tratar el tipo declarado bool como un int. Un valor booleano que es, obviamente, por defecto tratada como un byte y cuando la manipulación de bytes en el Intel mundo tiene que hacer cosas como signo-extender para llevar a ciertos anchos para ponerlo en la pila, etc. (Usted no puede empujar un byte.)

    Cuando el optimizador de vistas de la AST y hace su magia, sin embargo, parece que en el contexto que lo rodea y «sabe» cuando se puede reemplazar el código con algo más eficiente sin necesidad de cambiar la semántica. Por lo que «sabe» que puede utilizar un número entero en el parámetro y por lo tanto perder las conversiones innecesarias y ampliación.

    • Jaja, me gusta cómo el compilador simplemente devuelto 99, 99+58 = 157 = -99 (desbordamiento de firmado 8bits)… muy interesante.
    • Aunque me gustó eso. En primer lugar, me dijo: «¿dónde está -99» y de inmediato me di cuenta de que se va haciendo algo muy rizado.
    • l y b son sufijos utilizados en AT&T sólo sintaxis. Que sólo se refiere a las versiones de cmp el uso de 4 bytes (de largo) y de 1 byte (byte) operandos respectivamente. Donde no hay ninguna ambigüedad en la sintaxis de intel, convencionalmente el operando de memoria con la etiqueta de BYTE PTR, WORD PTR o DWORD PTR en lugar de poner el sufijo a en el código de operación.
    • Temporización de las tablas: agner.org/optimize Ambos operando-tamaños de cmp tienen el mismo rendimiento, y no hay ningún parcial-registro de sanciones por leer %dil. (Pero que no deja de sonar desde encontrareis con la creación de un parcial de registro de puesto mediante byte de tamaño and en AL como parte de branchlessly caso-mover de un tirón entre el 99 y el -99.)
  4. 13

    Con GCC 4.5 sobre Linux y Windows, al menos, sizeof(bool) == 1. En x86 y x86_64, no se puede pasar en menos de un propósito general de registro de la pena a una función (ya sea a través de la pila o de un registro, dependiendo de la convención de llamada, etc…).

    Para el código de tipo bool, cuando la onu-optimizado, en realidad va a algunos de longitud extraer ese valor bool desde el argumento de la pila (usando otra pila ranura para guardar ese byte). Es más complicado que simplemente tirando de un nativo de registro de tamaño variable.

    • Desde el C++03 estándar, §5.3.3/1: «sizeof(bool) y sizeof(wchar_t) son de aplicación definido.» Así diciendo sizeof(bool) == 1 no es estrictamente correcto, a menos que usted está hablando acerca de una versión específica de un compilador específico.
    • Muy correcto, editado, gracias.
  5. 9

    En el nivel de la máquina, no hay tal cosa como bool

    Muy pocos conjunto de instrucciones de arquitecturas de definir cualquier tipo de booleano operando tipo, aunque a menudo hay instrucciones que desencadenan una acción en valores distintos de cero. A la CPU, por lo general, todo lo que es uno de los tipos escalares o de una cadena de ellos.

    Un compilador y un dado ABI tendrá que elegir tamaños específicos para int y bool y cuando, como en tu caso, estas son de diferentes tamaños que pueden generar código ligeramente diferente, y en algunos niveles de la optimización de uno puede ser un poco más rápido.

    ¿Por qué es bool byte en muchos sistemas?

    Es más seguro elegir un char tipo bool, porque alguien podría hacer una gran variedad de ellos.

    Actualización: por «más seguro», quiero decir: para el compilador y la biblioteca de los ejecutores. No estoy diciendo que la gente necesita para reimplementar el tipo de sistema.

    • +1 Imagine la sobrecarga en los sistemas x86 si bool estuvieron representados por bits; por lo byte será una buena solución de compromiso para la velocidad/datos de la compacidad en muchas implementaciones.
    • Creo que él no dijo: «uso char en lugar de bool«, sino que simplemente utiliza «char tipo» para significar «1 byte» cuando se refiere al tamaño del compilador elige para bool objetos.
    • Oh, seguro, no me refiero a que cada programa debe elegir, se me acaba de adelantar una justificación de por qué el sistema de tipo bool es de 1 byte.
    • Ah, eso tiene sentido.
  6. 7

    Sí, la discusión de la diversión. Pero sólo la prueba:

    El código de la prueba:

    #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;
    }

    Compilado en una de 64 bits de Ubuntu 10.10 portátil con:
    g++ -O3 -o /tmp/test_i /tmp/test_i.cpp

    Entero basado en la comparación:

    [email protected]:/tmp$ time /tmp/test_i 1 > /dev/null
    
    real    0m8.203s
    user    0m8.170s
    sys 0m0.010s
    [email protected]:/tmp$ time /tmp/test_i 1 > /dev/null
    
    real    0m8.056s
    user    0m8.020s
    sys 0m0.000s
    [email protected]:/tmp$ time /tmp/test_i 1 > /dev/null
    
    real    0m8.116s
    user    0m8.100s
    sys 0m0.000s

    Booleano prueba de impresión /sin comentarios (y entero comentado):

    [email protected]:/tmp$ time /tmp/test_i 1 > /dev/null
    
    real    0m8.254s
    user    0m8.240s
    sys 0m0.000s
    [email protected]:/tmp$ time /tmp/test_i 1 > /dev/null
    
    real    0m8.028s
    user    0m8.000s
    sys 0m0.010s
    [email protected]:/tmp$ time /tmp/test_i 1 > /dev/null
    
    real    0m7.981s
    user    0m7.900s
    sys 0m0.050s

    Que son el mismo con 1 asignación y 2 comparaciones de cada uno de los bucles más de 30 millones de bucles. Encontrar algo más para optimizar. Por ejemplo, no usar strcmp innecesariamente. 😉

  7. 0

    Acercarse a su pregunta de dos maneras diferentes:

    Si usted está hablando específicamente de C++ o cualquier otro lenguaje de programación que se va a producir código de la asamblea para que la materia, estamos obligados a qué código, el compilador generará en ASM. También estamos obligados a la representación de la verdadera y falsa en c++. Un entero tendrá que ser almacenado en 32 bits, y que simplemente podría utilizar un byte para almacenar la expresión booleana. Asm fragmentos de instrucciones condicionales:

    Para el entero:

      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

    Para el bool:

      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

    Así que por eso la comparación de velocidad es tan compilar dependiente. En el caso anterior, el bool sería un poco rápido ya que cmp implicaría una resta para la configuración de las banderas. También se contradice con lo que el compilador genera.

    Otro enfoque, mucho más simple, es mirar a la lógica de la expresión propia y que trate de no preocuparse acerca de cómo el compilador traduce el código, y creo que esta es una manera mucho más saludable de pensar. Todavía creo que, en última instancia, que el código generado por el compilador es en realidad tratando de dar una información veraz de la resolución. Lo que quiero decir es que, tal vez, si usted incremento de los casos de prueba en la instrucción if y palo con booleana en un lado y entero en otro, el compilador hará que el código generado se ejecutará más rápido con expresiones booleanas en el nivel de la máquina.

    Estoy considerando este es un concepto en cuestión, así que me voy a dar una conceptuales respuesta. Esta discusión me recuerda a las discusiones que suelen tener acerca de si o no el código de la eficiencia se traduce en menos líneas de código en la asamblea. Parece que este concepto es generalmente aceptado como verdadero. Teniendo en cuenta que el seguimiento de cómo de rápido el ALU va a manejar cada afirmación no es viable, la segunda opción sería centrarse en los saltos y compara en la asamblea. Cuando ese es el caso, la distinción entre expresiones booleanas o enteros en el código que se presenta se convierte en lugar de representante. El resultado de una expresión en C++ devolverá un valor que a continuación se da una representación. En la asamblea, por otro lado, los saltos y las comparaciones se basan en valores numéricos, independientemente de qué tipo de expresión se está evaluando en C++ si la instrucción. Es importante en estas preguntas para recordar que puramente logicical declaraciones como éstas terminan con una enorme sobrecarga computacional, aunque un único bit sería capaz de lo mismo.

Dejar respuesta

Please enter your comment!
Please enter your name here