Markus**: Vergleichsoperator vs. Bitoperator

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**

  1. 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?
    
  2. 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

    1. Ebenfalls "Moin!",

      Moin!

      Nein, die zwei Ausdrücke sind nicht identisch.

      Stimmt, danke für das Beispiel!

      Antwort erübrigt sich. :)

      • Sven Rautenberg

      Markus**

      1. 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!

        1. 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

          1. 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

            --
            Liebet eure Feinde - vielleicht schadet das ihrem Ruf.
            Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
            1. 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

              --
              Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
              1. 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

                --
                Zivilisation bedeutet, dass die Eskimos warme Wohnungen bekommen und dann arbeiten müssen, damit sie sich einen Kühlschrank leisten können.
                Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
  3. 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