Welcher Wert ist besser zu verwenden? Boolean true oder Integer 1?
Das obige Thema hat mich dazu gebracht, einige Experimente mit bool
und int
in if
gutem Zustand durchzuführen . Aus Neugier schrieb ich dieses Programm:
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
generiert asm-Code für jede Funktion wie folgt:
ASM-Code für
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
ASM-Code für
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
Überraschenderweise g(bool)
generiert mehr asm
Anweisungen! Bedeutet das, dass if(bool)
das etwas langsamer ist als if(int)
? Früher dachte ich, dass bool
es speziell für die Verwendung in bedingten Anweisungen wie entwickelt wurde if
, also hatte ich erwartet g(bool)
, weniger asm-Anweisungen zu generieren, um dadurch g(bool)
effizienter und schneller zu werden.
BEARBEITEN:
Ich verwende derzeit kein Optimierungsflag. Aber selbst wenn es nicht vorhanden ist, warum es mehr Asm erzeugt, g(bool)
ist eine Frage, auf die ich nach einer vernünftigen Antwort suche. Ich sollte Ihnen auch sagen, dass das -O2
Optimierungsflag genau das gleiche asm generiert. Das ist aber nicht die Frage. Die Frage ist, was ich gestellt habe.
g(bool)
ist eine Frage, auf die ich nach einer vernünftigen Antwort suche.Antworten:
Für mich ergibt das Sinn. Ihr Compiler definiert a anscheinend
bool
als 8-Bit-Wert, und Ihr System-ABI verlangt, dass kleine (<32-Bit) Ganzzahlargumente auf 32-Bit "hochgestuft" werden, wenn sie auf den Aufrufstapel übertragen werden. Um a zu vergleichenbool
, generiert der Compiler Code, um das niedrigstwertige Byte des 32-Bit-Arguments, das g empfängt, zu isolieren, und vergleicht es mitcmpb
. Im ersten Beispiel verwendet dasint
Argument die vollen 32 Bits, die auf den Stapel geschoben wurden, sodass es einfach mit dem Ganzen verglichen wirdcmpl
.quelle
__int64
schneller sind alsint
? Oder behandelt die CPU eine 32-Bit-Ganzzahl mit 32-Bit-Befehlssätzen separat?Das Kompilieren mit
-03
ergibt für mich Folgendes:f:
G:
.. so stellt es im Wesentlichen den gleichen Code, mit Ausnahme
cmpl
vscmpb
. Dies bedeutet, dass der Unterschied, falls vorhanden, keine Rolle spielt. Nach nicht optimiertem Code zu urteilen ist nicht fair.Bearbeiten , um meinen Punkt zu verdeutlichen. Nicht optimierter Code dient dem einfachen Debuggen, nicht der Geschwindigkeit. Das Vergleichen der Geschwindigkeit von nicht optimiertem Code ist sinnlos.
quelle
cmpl
für das eine undcmpb
für das andere verwendet?bool
ein einzelnes Byte und anint
vier ist. Ich glaube nicht, dass es etwas Besonderes gibt.bool
als 8-Bit-Typ behandelt wird.char
per Definition ein Byte und die kleinste adressierbare Einheit.bool
Die Größe ist implementierungsdefiniert und kann 1, 4 oder 8 oder was auch immer sein. Compiler neigen jedoch dazu, es zu einem zu machen.Wenn ich dies mit einer Reihe vernünftiger Optionen kompiliere (speziell -O3), bekomme ich Folgendes:
Für
f()
:Für
g()
:Sie verwenden immer noch unterschiedliche Anweisungen für den Vergleich (
cmpb
für Boolesche vs.cmpl
für Int), aber ansonsten sind die Körper identisch. Ein kurzer Blick auf die Intel-Handbücher zeigt mir: ... nicht viel von irgendetwas. Es gibt keinecmpb
odercmpl
in den Intel-Handbüchern. Sie sind allecmp
und ich kann die Zeittabellen im Moment nicht finden. Ich vermute jedoch, dass es keinen Taktunterschied zwischen dem Vergleich eines sofortigen Bytes und dem Vergleich eines langen sofortigen Bytes gibt, sodass der Code für alle praktischen Zwecke identisch ist.bearbeitet, um basierend auf Ihrer Hinzufügung Folgendes hinzuzufügen
Der Grund, warum sich der Code im nicht optimierten Fall unterscheidet, ist, dass er nicht optimiert ist. (Ja, es ist zirkulär, ich weiß.) Wenn der Compiler den AST durchläuft und Code direkt generiert, "weiß" er nichts außer dem, was sich unmittelbar am AST befindet. An diesem Punkt fehlen alle erforderlichen Kontextinformationen zu wissen, dass es an diesem bestimmten Punkt den deklarierten Typ
bool
als behandeln kannint
. Ein Boolescher Wert wird offensichtlich standardmäßig als Byte behandelt. Wenn Sie Bytes in der Intel-Welt bearbeiten, müssen Sie beispielsweise die Zeichenerweiterung ausführen, um sie auf bestimmte Breiten zu bringen, damit sie auf den Stapel gelegt werden können usw. (Sie können kein Byte verschieben .)Wenn der Optimierer den AST betrachtet und seine Magie ausübt, betrachtet er den umgebenden Kontext und "weiß", wann er Code durch etwas Effizienteres ersetzen kann, ohne die Semantik zu ändern. Es "weiß" also, dass es eine Ganzzahl im Parameter verwenden kann und dadurch die unnötigen Konvertierungen und Erweiterungen verliert.
quelle
l
undb
sind Suffixe, die nur in der AT & T-Syntax verwendet werden. Sie beziehen sich nur auf Versionen dercmp
Verwendung von 4-Byte-Operanden (lang) bzw. 1-Byte-Operanden (Byte). Wo es eine Mehrdeutigkeit in Intel - Syntax ist in herkömmlicher Weise wird der Speicheroperand getaggt mitBYTE PTR
,WORD PTR
oderDWORD PTR
stattdessen ein Suffix auf dem Opcode des Setzens.cmp
haben die gleiche Leistung, und es gibt keine Teilregister -Strafen für das Lesen%dil
. (Aber das hält Clang nicht davon ab, auf amüsante Weise einen Teilregister-Stall zu erstellen, indem die Byte-Größeand
auf AL als Teil des verzweigten Fallwechsels zwischen 99 und -99 verwendet wird.)Zumindest mit GCC 4.5 unter Linux und Windows
sizeof(bool) == 1
. Unter x86 und x86_64 können Sie nicht weniger als den Wert eines Allzweckregisters an eine Funktion übergeben (ob über den Stapel oder ein Register, abhängig von der Aufrufkonvention usw.).Wenn der Code für bool nicht optimiert ist, wird er tatsächlich eine gewisse Länge lang, um diesen bool-Wert aus dem Argumentstapel zu extrahieren (wobei ein anderer Stapelsteckplatz zum Speichern dieses Bytes verwendet wird). Es ist komplizierter als nur eine native Variable in Registergröße zu ziehen.
quelle
sizeof(bool)
undsizeof(wchar_t)
sind implementierungsdefiniert. " Das Sprichwortsizeof(bool) == 1
ist also nicht genau richtig, es sei denn, Sie sprechen von einer bestimmten Version eines bestimmten Compilers.Auf Maschinenebene gibt es keinen Bool
Sehr wenige Befehlssatzarchitekturen definieren irgendeine Art von booleschem Operandentyp, obwohl es häufig Befehle gibt, die eine Aktion für Werte ungleich Null auslösen. Für die CPU ist normalerweise alles einer der Skalartypen oder eine Folge davon.
Ein gegebener Compiler und ein gegebenes ABI müssen bestimmte Größen wählen
int
und ,bool
und wenn, wie in Ihrem Fall, das sind verschiedene Größen sie leicht unterschiedlichen Code erzeugen können, und auf einigen Ebenen der Optimierung kann man etwas schneller sein.Warum ist Bool auf vielen Systemen ein Byte?
Es ist sicherer, einen
char
Typ für Bool zu wählen, da jemand eine wirklich große Anzahl von ihnen erstellen könnte.Update: von „sicherer“, ich meine: für den Compiler und Bibliothek Implementierer. Ich sage nicht, dass die Leute den Systemtyp neu implementieren müssen.
quelle
bool
er durch Bits dargestellt wird. Daher ist Byte in vielen Implementierungen ein guter Kompromiss für Geschwindigkeits- / Datenkompaktheit.char
stattbool
" gesagt, sondern einfach "char
Typ" verwendet, um "1 Byte" zu bedeuten, wenn er sich auf die Größe bezieht, die der Compiler fürbool
Objekte auswählt .Ja, die Diskussion macht Spaß. Aber testen Sie es einfach:
Testcode:
Kompiliert auf einem 64-Bit Ubuntu 10.10-Laptop mit: g ++ -O3 -o / tmp / test_i /tmp/test_i.cpp
Ganzzahliger Vergleich:
Boolescher Test / Druck unkommentiert (und Ganzzahl kommentiert):
Sie sind mit 1 Zuweisung und 2 Vergleichen pro Schleife über 30 Millionen Schleifen gleich. Finden Sie etwas anderes zu optimieren. Verwenden Sie strcmp beispielsweise nicht unnötig. ;)
quelle
Dies hängt hauptsächlich vom Compiler und der Optimierung ab. Hier gibt es eine interessante Diskussion (sprachunabhängig):
Benötigt "if ([bool] == true)" einen Schritt mehr als "if ([bool])"?
Schauen Sie sich auch diesen Beitrag an: http://www.linuxquestions.org/questions/programming-9/c-compiler-handling-of-boolean-variables-290996/
quelle
Gehen Sie Ihre Frage auf zwei verschiedene Arten an:
Wenn Sie speziell über C ++ oder eine Programmiersprache sprechen, die Assembler-Code erzeugt, sind wir an den Code gebunden, den der Compiler in ASM generiert. Wir sind auch an die Darstellung von wahr und falsch in c ++ gebunden. Eine Ganzzahl muss in 32 Bit gespeichert werden, und ich könnte einfach ein Byte verwenden, um den booleschen Ausdruck zu speichern. Asm-Snippets für bedingte Anweisungen:
Für die ganze Zahl:
Für den Bool:
Deshalb ist der Geschwindigkeitsvergleich so kompilierungsabhängig. Im obigen Fall wäre der Bool etwas schnell, da
cmp
dies eine Subtraktion zum Setzen der Flags implizieren würde. Es widerspricht auch dem, was Ihr Compiler generiert hat.Ein anderer Ansatz, der viel einfacher ist, besteht darin, die Logik des Ausdrucks selbst zu betrachten und sich keine Gedanken darüber zu machen, wie der Compiler Ihren Code übersetzt. Ich denke, dies ist eine viel gesündere Denkweise. Letztendlich glaube ich immer noch, dass der vom Compiler generierte Code tatsächlich versucht, eine wahrheitsgemäße Lösung zu finden. Was ich damit meine ist, dass der Compiler es möglicherweise so macht, dass der generierte Code mit booleschen Ausdrücken auf Maschinenebene schneller ausgeführt wird, wenn Sie die Testfälle in der if-Anweisung erhöhen und auf einer Seite boolesch und auf einer anderen ganzzahlig bleiben.
Ich denke, dies ist eine konzeptionelle Frage, also werde ich eine konzeptionelle Antwort geben. Diese Diskussion erinnert mich an Diskussionen, die ich häufig darüber habe, ob sich die Codeeffizienz in weniger Codezeilen in der Assembly niederschlägt oder nicht. Es scheint, dass dieses Konzept allgemein als wahr anerkannt wird. In Anbetracht der Tatsache, dass es nicht möglich ist, zu verfolgen, wie schnell die ALU jede Anweisung verarbeitet, besteht die zweite Option darin, sich auf Sprünge und Vergleiche in der Assembly zu konzentrieren. In diesem Fall wird die Unterscheidung zwischen booleschen Anweisungen oder Ganzzahlen in dem von Ihnen präsentierten Code ziemlich repräsentativ. Das Ergebnis eines Ausdrucks in C ++ gibt einen Wert zurück, der dann dargestellt wird. In der Montage dagegen Die Sprünge und Vergleiche basieren auf numerischen Werten, unabhängig davon, welche Art von Ausdruck bei Ihrer C ++ if-Anweisung ausgewertet wurde. Bei diesen Fragen ist es wichtig, sich daran zu erinnern, dass rein logische Aussagen wie diese einen enormen Rechenaufwand verursachen, obwohl ein einzelnes Bit dasselbe kann.
quelle