Lequel est le plus rapide: si (bool) ou si(int)?

quelle valeur est la meilleure à utiliser? Booléen vrai ou entier 1?

le sujet ci-dessus m'a fait faire quelques expériences avec bool et int dans if condition. Donc, juste par curiosité, j'ai écrit ce programme:

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 génère un code asm pour chaque fonction comme suit:

  • code asm pour f(int)

    __Z1fi:
       LFB0:
             pushl  %ebp
       LCFI0:
              movl  %esp, %ebp
       LCFI1:
              cmpl  "151910920", 8(%ebp)
              je    L2
              movl  , %eax
              jmp   L3
       L2:
              movl  $-99, %eax
       L3:
              leave
       LCFI2:
              ret
    
  • code asm pour g(bool)

    __Z1gb:
       LFB1:
              pushl %ebp
       LCFI3:
              movl  %esp, %ebp
       LCFI4:
              subl  , %esp
       LCFI5:
              movl  8(%ebp), %eax
              movb  %al, -4(%ebp)
              cmpb  "151920920", -4(%ebp)
              je    L5
              movl  , %eax
              jmp   L6
       L5:
              movl  $-99, %eax
       L6:
              leave
       LCFI6:
              ret
    

étonnamment, g(bool) génère plus asm instructions! Cela signifie-t-il que if(bool) est un peu plus lent que if(int) ? Je pensais que bool était spécialement conçu pour être utilisé dans une déclaration conditionnelle telle que if , donc je m'attendais à ce que g(bool) génère moins d'asm instructions, ce qui rend g(bool) plus efficace et rapide.

EDIT:

Je n'utilise aucun drapeau d'optimisation pour le moment. Mais même en son absence, pourquoi génère-t-il plus de MSA pour g(bool) est une question pour laquelle je cherche une réponse raisonnable. Je dois aussi vous dire que le drapeau d'optimisation -O2 génère exactement le même asm. Mais ce n'est pas la question. La question est de savoir ce que j'ai demandé.


85
demandé sur Community 2011-04-23 19:07:18

8 réponses

a du sens pour moi. Votre compilateur définit apparemment un bool comme une valeur 8-bit, et votre ABI système l'exige pour" promouvoir " de petits arguments (< 32-bit) entiers à 32-bit en les poussant sur la pile d'appels. Ainsi, pour comparer un bool , le compilateur génère du code pour isoler l'octet le moins significatif de l'argument de 32 bits que g reçoit, et le compare avec cmpb . Dans le premier exemple, l'argument int utilise les 32 bits qui ont été poussés sur la pile, donc il se compare simplement par rapport à l'ensemble avec cmpl .

94
répondu Sherm Pendley 2011-04-23 15:20:54

compilant avec -03 donne ce qui suit pour moi:

f:

    pushl   %ebp
    movl    %esp, %ebp
    cmpl    , 8(%ebp)
    popl    %ebp
    sbbl    %eax, %eax
    andb    , %al
    addl    , %eax
    ret

g:

    pushl   %ebp
    movl    %esp, %ebp
    cmpb    , 8(%ebp)
    popl    %ebp
    sbbl    %eax, %eax
    andb    , %al
    addl    , %eax
    ret

.. donc il compile essentiellement le même code, sauf pour cmpl vs cmpb . Cela signifie que la différence, s'il y en a, n'a pas d'importance. A en juger par le code non optimisé n'est pas juste.


modifier pour clarifier mon point. Le code non optimisé est pour le débogage simple, pas pour la vitesse. Comparer la vitesse de code non optimisé est insensé.

76
répondu Alexander Gessler 2018-05-04 01:10:08

quand je compilerai ceci avec un ensemble d'options (spécifiquement-O3), voici ce que j'obtiens:

pour f() :

        .type   _Z1fi, @function
_Z1fi:
.LFB0:
        .cfi_startproc
        .cfi_personality 0x3,__gxx_personality_v0
        cmpl    , %edi
        sbbl    %eax, %eax
        andb    , %al
        addl    , %eax
        ret
        .cfi_endproc

pour g() :

        .type   _Z1gb, @function
_Z1gb:
.LFB1:
        .cfi_startproc
        .cfi_personality 0x3,__gxx_personality_v0
        cmpb    , %dil
        sbbl    %eax, %eax
        andb    , %al
        addl    , %eax
        ret
        .cfi_endproc

ils utilisent encore des instructions différentes pour la comparaison ( cmpb pour booléen vs. cmpl pour int), mais autrement les corps sont identiques. Un rapide coup d'oeil sur les manuels Intel me dit: ... pas beaucoup de choses. Il n'y a pas chose comme cmpb ou cmpl dans les manuels Intel. Ils sont tous cmp et je ne trouve pas les tables de temps pour le moment. Je devine, cependant, qu'il n'y a pas de différence d'horloge entre comparer un octet immédiat par rapport à comparer un long immédiat, donc pour toutes les fins pratiques le code est identique.


modifié pour ajouter ce qui suit basé sur votre ajout

la raison pour laquelle le le code est différent dans le cas non optimisé est qu'il est non optimisé. (Oui, elle est circulaire, je sais. Quand le compilateur parcourt le AST et génère du code directement, il ne "sait" rien sauf ce qui est au point immédiat du AST dans lequel il se trouve. À ce moment-là, il ne dispose pas de toutes les informations contextuelles nécessaires pour savoir qu'à ce moment précis il peut traiter le type déclaré bool comme un int . Un booléen est évidemment par défaut traité comme un octet et lors de la manipulation des octets dans le Intel world vous devez faire des choses comme sign-extend pour l'amener à certaines largeurs pour le mettre sur la pile, etc. (Vous ne pouvez pas pousser un octet.)

quand l'optimiseur voit L'AST et fait sa magie, cependant, il regarde le contexte environnant et "sait" quand il peut remplacer le code avec quelque chose de plus efficace sans changer la sémantique. Donc il "sait" qu'il peut utiliser un entier dans le paramètre et ainsi perdre les conversions inutiles et l'élargissement.

26
répondu JUST MY correct OPINION 2011-04-23 15:31:14

avec GCC 4.5 sur Linux et Windows au moins, sizeof(bool) == 1 . Sur x86 et x86_64, vous ne pouvez pas passer en moins de la valeur d'un registre universel à une fonction (que ce soit via la pile ou un registre en fonction de la convention d'appel, etc...).

ainsi le code pour bool, quand non-optimisé, va réellement à une certaine longueur pour extraire cette valeur de bool de la pile d'argument (en utilisant une autre fente de pile pour sauver ce byte). C'est plus compliqué que tirer un natif variable de la taille d'un registre.

13
répondu Mat 2011-04-23 15:42:32

au niveau de la machine, il n'y a pas de bool

très peu d'architectures d'instructions définissent un type d'opérande booléen, bien qu'il y ait souvent des instructions qui déclenchent une action sur des valeurs non-nulles. Pour le CPU, habituellement, tout est un des types scalaires ou une chaîne d'entre eux.

un compilateur et un ABI donnés devront choisir des tailles spécifiques pour int et bool et quand, comme dans votre cas, ce sont des tailles différentes, ils peuvent générer du code légèrement différent, et à certains niveaux d'optimisation on peut être légèrement plus rapide.

pourquoi bool est un byte sur de nombreux systèmes?

il est plus sûr de choisir un char type pour bool parce que quelqu'un pourrait faire un très large éventail d'entre eux.

mise à Jour: par plus "sécuritaire", , je veux dire: pour le compilateur et de la bibliothèque des réalisateurs. Je ne dis pas que les gens ont besoin de réimplanter le type de système.

9
répondu DigitalRoss 2011-06-02 05:49:32

oui, la discussion est amusante. Mais il suffit de le tester:

code D'essai:

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

compilé sur un 64-bit Ubuntu 10.10 ordinateur portable avec: g++ -O3-o /tmp/test_i /tmp/test_i.cpp

basée sur des Entiers de comparaison:

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

Boolean test / imprimer sans commentaire (entier et commenté):

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

ils sont les mêmes avec 1 assignation et 2 comparaisons chaque boucle sur 30 millions de boucles. Trouvez autre chose à optimiser. Par exemple, n'utilisez pas strcmp inutilement. ;)

7
répondu dannysauer 2011-04-24 05:32:53

cela dépendra principalement du compilateur et de l'optimisation. Il y a une discussion intéressante (langue agnostique) ici:

Ne "si ([bool] == true)" nécessitent une étape de plus que "si ([bool])"?

aussi, jetez un oeil à ce post: http://www.linuxquestions.org/questions/programming-9/c-compiler-handling-of-boolean-variables-290996 /

2
répondu Aleadam 2017-05-23 12:32:28

aborder votre question de deux façons différentes:

si vous parlez spécifiquement de C++ ou de tout autre langage de programmation qui produira du code d'assemblage pour cette matière, nous sommes liés au code que le compilateur générera dans ASM. Nous sommes également liés à la représentation de true et false en c++. Un entier devra être stocké en 32 bits, et je pourrais simplement utiliser un octet pour stocker l'expression booléenne. Asm extraits de code pour les instructions conditionnelles:

pour le nombre entier:

  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

pour le 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

donc, c'est pourquoi la comparaison de vitesse est si dépendante de la compilation. Dans le cas ci-dessus, le bool serait légèrement rapide puisque cmp impliquerait une soustraction pour définir les drapeaux. Il est également en contradiction avec ce que votre compilateur a généré.

une autre approche, beaucoup plus simple, consiste à regarder la logique de l'expression par elle-même et à ne pas essayer s'inquiéter de la façon dont le compilateur va traduire votre code, et je pense que c'est une façon de penser beaucoup plus saine. Je crois toujours, en fin de compte, que le code généré par le compilateur essaye en fait de donner une résolution vraie. Ce que je veux dire c'est que, peut-être que si vous augmentez les cas de test dans la déclaration if et que vous vous en tenez à boolean d'un côté et à integer de l'autre, le compilateur le fera de sorte que le code généré exécutera plus rapidement avec des expressions booléennes au niveau de la machine.

je considère que c'est une question conceptuelle, donc je vais donner une réponse conceptuelle. Cette discussion me rappelle les discussions que j'ai souvent sur la question de savoir si l'efficacité du code se traduit par moins de lignes de code dans l'assemblage. Il semble que ce concept est généralement accepté comme étant vrai. Étant donné qu'il n'est pas viable de suivre à quelle vitesse l'ALU traitera chaque déclaration, la deuxième option serait de se concentrer sur les sauts et les comparaisons dans l'assemblage. Lorsque c'est le cas, la distinction entre les énoncés booléens ou entiers dans le code que vous avez présenté devient plutôt représentative. Le résultat d'une expression en C++ retournera une valeur qui recevra alors une représentation. Dans assembly, d'autre part, les sauts et les comparaisons seront basés en valeurs numériques indépendamment du type d'expression qui a été évalué retour à vous c++ IF statement. Il est important sur ces questions de se rappeler que des déclarations purement logiques comme celles-ci finissent par un énorme overhead de calcul, même si un seul bit serait capable de la même chose.

0
répondu Artie 2011-12-12 07:30:38