Vergleichsoperator vs. Bitoperator
Markus**
- programmiertechnik
1 ChrisB2 Sven Rautenberg1 Stefanie M.
Hallo Forum!
Angenommen ich möchte zwei Datensätze vom Typ integer miteinander vergleichen, also würden die beiden folgenden ausdrücke 1 bzw. true zurückgeben, oder?
if (d1==d2) {/* .... */}
if (d1&d2) {/* .... */}
Behandelt der Compiler diese beiden Varianten identisch, oder macht er unterschiede?
Dank und Gruß, Markus**
Hi,
Angenommen ich möchte zwei Datensätze vom Typ integer miteinander vergleichen, also würden die beiden folgenden ausdrücke 1 bzw. true zurückgeben, oder?
if (d1==d2) {/* .... */}
if (d1&d2) {/* .... */}
Das kommt wohl auf die Inhalte von d1 und d2 an, ob das letztendlich true oder false ergibt.
15&15 ergibt 15.
Das \*evaluiert\* üblicherweise zwei true, aber es \*ist\* nicht true - und 1 schon gar nicht.
MfG ChrisB
--
RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
Moin!
Angenommen ich möchte zwei Datensätze vom Typ integer miteinander vergleichen, also würden die beiden folgenden ausdrücke 1 bzw. true zurückgeben, oder?
Nein, die zwei Ausdrücke sind nicht identisch.
d1 = 255
d2 = 1
d1 & d2 = 1 => true
d1 == d2 => false
Behandelt der Compiler diese beiden Varianten identisch, oder macht er unterschiede?
Antwort erübrigt sich. :)
- Sven Rautenberg
Ebenfalls "Moin!",
Moin!
Nein, die zwei Ausdrücke sind nicht identisch.
Stimmt, danke für das Beispiel!
Antwort erübrigt sich. :)
- Sven Rautenberg
Markus**
Hi!
Nein, die zwei Ausdrücke sind nicht identisch.
Stimmt, danke für das Beispiel!
Prüfst du deine Hypothesen nicht anhand von ein paar kleinen Programmbeispielen?
Mit bitweisem XOR kann man das Ziel, wenn auch mit gegensätzlichem Ergebnis, erreichen. Zwei gleiche Werte löschen sich zu 0 aus, alles andere ergibt ein von 0 verschiedenes Ergebnis.
Lo!
Moin!
Nein, die zwei Ausdrücke sind nicht identisch.
Stimmt, danke für das Beispiel!Prüfst du deine Hypothesen nicht anhand von ein paar kleinen Programmbeispielen?
Mit bitweisem XOR kann man das Ziel, wenn auch mit gegensätzlichem Ergebnis, erreichen. Zwei gleiche Werte löschen sich zu 0 aus, alles andere ergibt ein von 0 verschiedenes Ergebnis.
Man sollte bei solchen Gedanken aber immer auch die Frage nach der zugehörigen Motivation stellen: Warum will da jemand eine offensichtliche Operation (Vergleich) durch eine nicht-offensichtliche Operation (Bitoperator als Ersatz für einen Vergleich) einsetzen?
Die einzige Antwort, die ich an dieser Stelle gelten lassen würde: Performance.
Wobei derjenige auch dafür erst den Beweis antreten muss, indem er den tatsächlich generierten Bytecode des Compilers für beide Varianten analysiert und anhand von Taktzyklen für jedes denkbare Input-Szenario beweist, dass die eine Variante besser ist, als die andere.
Im Grunde genommen diskutieren wir hier also den extrem seltenen Fall, dass jemand auf Hardware mit beschränkter Leistungsfähigkeit (im Vergleich zur erledigenden Aufgabe) Mikrooptimierung betreiben will.
Im allgemeinen hingegen werden beide Varianten vermutlich kaum 1% Unterschied in der Ausführungsgeschwindigkeit haben, so dass vollkommen andere Effekte in den Vordergrund treten, nämlich: Ist der "optimierte" Code lesbarer, als der offensichtliche Code? Das hat auf die Ausführungsperformance des Codes zwar keinen Einfluß, sehr wohl aber auf die Performance des Programmierers, der sich mit dem "optimierten" Code herumschlagen muss. Ihn also mehrfach nach seiner Erstellung lesen muss und immer wieder denkt: "WTF? Was tut das?" Durch solche Performancebremsen mit unleserlichem Code dürften im Endeffekt viel mehr Kosten generiert werden, als durch die gewonnene Performance einsparbar wären. Spätestens wenn sich durch diese obskuren Operationen Fehler einschleichen.
- Sven Rautenberg
Hallo,
Man sollte bei solchen Gedanken aber immer auch die Frage nach der zugehörigen Motivation stellen: Warum will da jemand eine offensichtliche Operation (Vergleich) durch eine nicht-offensichtliche Operation (Bitoperator als Ersatz für einen Vergleich) einsetzen?
Die einzige Antwort, die ich an dieser Stelle gelten lassen würde: Performance.
d'accord - aber alle mir bekannten Prozessoren brauchen für einen Integer-Vergleich (8, 16 oder 32bit, je nach Architektur) exakt gleich lange wie für eine logische Operation mit den gleichen Operanden. Es müsste also schon eine sehr spezielle Hardware-Plattform sein, auf der dieses Argument zieht.
Es sei denn, man betrachtet den Vergleich im Zusammenhang mit einer nachfolgenden Operation, bei der man sich im Fall der Identität das Laden der Konstanten 0 in ein Prozessorregister sparen kann.
Tatsächlich nutzen manche x86-Compiler aus, dass diese CPUs interne logische oder arithmetische Operationen wesentlich schneller ausführen können als Speicherzyklen. Sollte ein solcher Compiler also die "Absicht" haben, das Prozessorregister EDX mit dem Wert 0 zu laden, wird er nicht die Instruktion
MOV EDX, 0x00000000
erzeugen, sondern wahrscheinlich eher
XOR EDX, EDX
Denn diese Instruktion braucht nicht nur weniger Bytes im Code (beide Operanden sind Register, ihre "Adresse" ist implizit im Opcode enthalten), sondern wird deswegen auch schneller ausgeführt (weniger Lesezyklen), sofern der Code nicht schon im Cache ist.
Wobei derjenige auch dafür erst den Beweis antreten muss, indem er den tatsächlich generierten Bytecode des Compilers für beide Varianten analysiert und anhand von Taktzyklen für jedes denkbare Input-Szenario beweist, dass die eine Variante besser ist, als die andere.
Ich fürchte, den wird uns derjenige schuldig bleiben (müssen).
Ciao,
Martin
Moin Moin!
Es sei denn, man betrachtet den Vergleich im Zusammenhang mit einer nachfolgenden Operation, bei der man sich im Fall der Identität das Laden der Konstanten 0 in ein Prozessorregister sparen kann.
Man kann, analog zu XOR reg,reg, OR reg,reg dazu benutzen, die Flags entsprechend dem Inhalt des Registers reg zu setzen, ohne reg zu verändern oder irgendwelche Konstanten bemühen zu müssen. Insbesondere für das Zero-Flag wird das gerne benutzt -- mindestens auf den x86-Prozessoren und analog auf dem Z80.
Ansonsten gibt es aus alten DOS-Zeiten noch etwas krude Tests, z.B. OR AX,DX, um ein DWORD (long int oder long pointer) in DX:AX auf 0 / nicht 0 zu testen. Etwas länger auch als LES DI,somepointer; MOV AX,ES; OR AX,DI; JZ zeropointer.
Alexander
Hallo,
Man kann, analog zu XOR reg,reg, OR reg,reg dazu benutzen, die Flags entsprechend dem Inhalt des Registers reg zu setzen, ohne reg zu verändern oder irgendwelche Konstanten bemühen zu müssen. Insbesondere für das Zero-Flag wird das gerne benutzt -- mindestens auf den x86-Prozessoren und analog auf dem Z80.
ja, ist mir auch geläufig.
Ansonsten gibt es aus alten DOS-Zeiten noch etwas krude Tests, z.B. OR AX,DX, um ein DWORD (long int oder long pointer) in DX:AX auf 0 / nicht 0 zu testen.
Das ist doch nichts "krudes", sondern pretty straightforward. Für mich intuitiv, wenn ich zwei Werte (oder zwei Register) auf 0 testen will.
Etwas länger auch als LES DI,somepointer; MOV AX,ES; OR AX,DI; JZ zeropointer.
Ist aber nur dann günstig, wenn man den Pointer in ES:DI sowieso braucht. Andernfalls sind passend gesetzte Segmentregister meist kostbar, man mag sie ungern überschreiben. Dann schon eher:
MOV AX,[somepointer+0]
OR AX,[somepointer+2]
JZ isnull
Das braucht wahrscheinlich auch nicht länger, verschont aber die Segmentregister.
So long,
Martin
Hi,
Behandelt der Compiler diese beiden Varianten identisch, oder macht er unterschiede?
Abgesehen davon, dass die beiden Operatoren nicht das gleiche Ergebnis liefern, gibt es "den Compiler" ebenso wenig wie "den Russen"...
Gruss
Stefanie