Quelle valeur est préférable d'utiliser? Boolean true ou Integer 1?
Le sujet ci-dessus m'a fait faire des expériences avec bool
et int
en if
condition. Alors 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 du code asm pour chaque fonction comme suit:
code asm pour
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
code asm pour
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
Étonnamment, g(bool)
génère plus d' asm
instructions! Cela signifie-t-il que if(bool)
c'est un peu plus lent que if(int)
? J'avais l'habitude de penser qu'il bool
était spécialement conçu pour être utilisé dans des instructions conditionnelles telles que if
, donc je m'attendais g(bool)
à générer moins d'instructions asm, ce qui rendrait g(bool)
plus efficace et plus rapide.
ÉDITER:
Je n'utilise aucun indicateur d'optimisation pour le moment. Mais même son absence, pourquoi génère-t-elle plus d'ASM pour g(bool)
est une question pour laquelle je cherche une réponse raisonnable. Je dois également vous dire que l' -O2
indicateur d'optimisation génère exactement le même asm. Mais ce n'est pas la question. La question est ce que j'ai demandé.
g(bool)
une question pour laquelle je cherche une réponse raisonnable.Réponses:
Cela a du sens pour moi. Votre compilateur définit apparemment a
bool
comme une valeur 8 bits, et votre système ABI lui demande de "promouvoir" les petits arguments entiers (<32 bits) en 32 bits lors de leur insertion dans la pile d'appels. Donc, pour comparer abool
, le compilateur génère du code pour isoler l'octet le moins significatif de l'argument 32 bits que g reçoit et le compare aveccmpb
. Dans le premier exemple, l'int
argument utilise les 32 bits complets qui ont été poussés sur la pile, il se compare donc simplement à l'ensemble aveccmpl
.la source
__int64
dire plus rapide queint
? Ou le CPU traite séparément un entier 32 bits avec des jeux d'instructions 32 bits?Compiler avec
-03
me donne ce qui suit:F:
g:
.. il compile essentiellement au même code, à l' exception
cmpl
vscmpb
. Cela signifie que la différence, s'il y en a, n'a pas d'importance. Juger par un code non optimisé n'est pas juste.Modifier pour clarifier mon point. Le code non optimisé sert au débogage simple, pas à la vitesse. Comparer la vitesse d'un code non optimisé est insensé.
la source
cmpl
pour l'un etcmpb
pour l'autre?bool
est un octet et anint
est quatre. Je ne pense pas qu'il y ait rien de plus spécial que ça.bool
comme un type 8 bits.char
, qui est un octet par définition, et est la plus petite unité adressable.bool
La taille de est définie par l'implémentation et peut être 1, 4 ou 8, ou autre. Cependant, les compilateurs ont tendance à en faire un.Lorsque je compile cela avec un ensemble d'options raisonnable (en particulier -O3), voici ce que j'obtiens:
Pour
f()
:Pour
g()
:Ils utilisent toujours des instructions différentes pour la comparaison (
cmpb
pour booléen vscmpl
pour int), mais sinon les corps sont identiques. Un rapide coup d'œil aux manuels d'Intel me dit: ... pas grand-chose. Il n'y a rien de tel quecmpb
oucmpl
dans les manuels Intel. Ils sont touscmp
et je ne trouve pas les horaires pour le moment. Je suppose, cependant, qu'il n'y a pas de différence d'horloge entre comparer un octet immédiat et comparer un long immédiat, donc à toutes fins pratiques, le code est identique.modifié pour ajouter ce qui suit en fonction de votre ajout
La raison pour laquelle le code est différent dans le cas non optimisé est qu'il n'est pas optimisé. (Oui, c'est circulaire, je sais.) Lorsque le compilateur parcourt l'AST et génère directement du code, il ne «sait» rien sauf ce qui se trouve au point immédiat de l'AST dans lequel il se trouve. À ce stade, il lui manque toutes les informations contextuelles nécessaires pour savoir qu'à ce stade précis, il peut traiter le type déclaré
bool
comme unint
. Un booléen est évidemment traité par défaut comme un octet et lors de la manipulation d'octets dans le monde Intel, 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 par quelque chose de plus efficace sans changer la sémantique. Ainsi, il "sait" qu'il peut utiliser un entier dans le paramètre et ainsi perdre les conversions et élargissements inutiles.
la source
l
etb
sont des suffixes utilisés dans la syntaxe AT&T uniquement. Ils se réfèrent simplement à des versionscmp
utilisant respectivement des opérandes de 4 octets (longs) et 1 octets (octets). Là où il y a une ambiguïté dans la syntaxe d'Intel, l'opérande de mémoire est classiquement étiqueté avecBYTE PTR
,WORD PTR
ouDWORD PTR
au lieu de mettre un suffixe sur l'opcode.cmp
ont les mêmes performances, et il n'y a pas de pénalités de registre partiel pour la lecture%dil
. (Mais cela n'empêche pas de créer de manière amusante un blocage de registre partiel en utilisant la taille d'octetand
sur AL dans le cadre d'un retournement de cas sans branche entre 99 et -99.)Avec GCC 4.5 sur Linux et Windows au moins,
sizeof(bool) == 1
. Sur x86 et x86_64, vous ne pouvez pas passer moins que la valeur d'un registre à usage général à une fonction (que ce soit via la pile ou un registre en fonction de la convention d'appel etc ...).Ainsi, le code de bool, lorsqu'il n'est pas optimisé, va en fait à une certaine longueur pour extraire cette valeur booléenne de la pile d'arguments (en utilisant un autre emplacement de pile pour enregistrer cet octet). C'est plus compliqué que de simplement extraire une variable native de la taille d'un registre.
la source
sizeof(bool)
etsizeof(wchar_t)
sont définis par l'implémentation. " Cesizeof(bool) == 1
n'est pas strictement correct à moins que vous ne parliez d'une version spécifique d'un compilateur spécifique.Au niveau de la machine, il n'y a pas de booléen
Très peu d'architectures de jeux d'instructions définissent une sorte de 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 processeur, généralement, tout est l'un des types scalaires ou une chaîne d'entre eux.
Un compilateur donné et un ABI donné devront choisir des tailles spécifiques pour
int
etbool
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, un peut être légèrement plus rapide.Pourquoi booléen est-il un octet sur de nombreux systèmes?
Il est plus sûr de choisir un
char
type pour booléen car quelqu'un pourrait en créer un très grand nombre.Mise à jour: par «plus sûr», je veux dire: pour les implémenteurs de compilateurs et de bibliothèques. Je ne dis pas que les gens doivent réimplémenter le type de système.
la source
bool
était représenté par des bits; donc byte sera un bon compromis pour la vitesse / la compacité des données dans de nombreuses implémentations.char
place debool
" mais à la place simplement utilisé "char
type" pour signifier "1 octet" en se référant à la taille que le compilateur choisit pour lesbool
objets.Ouais, la discussion est amusante. Mais testez-le simplement:
Code de test:
Compilé sur un ordinateur portable Ubuntu 10.10 64 bits avec: g ++ -O3 -o / tmp / test_i /tmp/test_i.cpp
Comparaison basée sur des nombres entiers:
Test booléen / impression non commentée (et entier commenté):
Ils sont les mêmes avec 1 affectation et 2 comparaisons, chaque boucle sur 30 millions de boucles. Trouvez autre chose à optimiser. Par exemple, n'utilisez pas strcmp inutilement. ;)
la source
Cela dépendra principalement du compilateur et de l'optimisation. Il y a une discussion intéressante (indépendante de la langue) ici:
Est-ce que "if ([bool] == true)" nécessite une étape de plus que "if ([bool])"?
Jetez également un œil à cet article: http://www.linuxquestions.org/questions/programming-9/c-compiler-handling-of-boolean-variables-290996/
la source
Aborder votre question de deux manières différentes:
Si vous parlez spécifiquement de C ++ ou de tout autre langage de programmation qui produira du code d'assemblage pour cette question, nous sommes liés au code que le compilateur générera dans ASM. Nous sommes également liés à la représentation de vrai et faux en c ++. Un entier devra être stocké en 32 bits, et je pourrais simplement utiliser un octet pour stocker l'expression booléenne. Extraits ASM pour les instructions conditionnelles:
Pour l'entier:
Pour le booléen:
C'est pourquoi la comparaison de vitesse est si dépendante de la compilation. Dans le cas ci-dessus, la valeur booléenne serait légèrement rapide car
cmp
impliquerait une soustraction pour définir les indicateurs. Cela contredit également ce que votre compilateur a généré.Une autre approche, beaucoup plus simple, consiste à examiner la logique de l'expression seule et à ne pas vous soucier de la façon dont le compilateur traduira 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 essaie en fait de donner une résolution véridique. Ce que je veux dire, c'est que, peut-être que si vous augmentez les cas de test dans l'instruction if et que vous vous en tenez avec booléen d'un côté et entier dans un autre, le compilateur fera en sorte que le code généré s'exécute plus rapidement avec des expressions booléennes au niveau de la machine.
Je considère que c'est une question conceptuelle, je vais donc donner une réponse conceptuelle. Cette discussion me rappelle les discussions que j'ai couramment sur la question de savoir si l'efficacité du code se traduit ou non par moins de lignes de code dans l'assemblage. Il semble que ce concept soit généralement accepté comme étant vrai. Étant donné que le suivi de la vitesse à laquelle l'ALU traitera chaque instruction n'est pas viable, 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 instructions booléennes ou les entiers dans le code que vous avez présenté devient plutôt représentative. Le résultat d'une expression en C ++ renverra une valeur qui recevra ensuite une représentation. En montage, en revanche, les sauts et les comparaisons seront basés sur des valeurs numériques quel que soit le type d'expression qui a été évalué dans votre instruction if C ++. Il est important sur ces questions de se rappeler que des déclarations purement logiques comme celles-ci se retrouvent avec une surcharge de calcul énorme, même si un seul bit serait capable de la même chose.
la source