Ist dieser XOR-Wert-Swap-Algorithmus noch in Gebrauch oder nützlich?

25

Als ich anfing zu arbeiten, zeigte mir ein Mainframe Assembler-Programmierer, wie sie zu Werten wechseln, ohne den traditionellen Algorithmus von:

a = 0xBABE
b = 0xFADE

temp = a
a = b
b = temp

Was sie verwendeten, um zwei Werte zu tauschen - von einem Bit zu einem großen Puffer - war:

a = 0xBABE
b = 0xFADE

a = a XOR b
b = b XOR a
a = a XOR b

jetzt

b == 0xBABE
a == 0xFADE

Dadurch wurde der Inhalt von 2 Objekten ausgetauscht, ohne dass ein dritter temporärer Speicherplatz erforderlich war.

Meine Frage ist: Ist dieser XOR-Swap-Algorithmus noch in Gebrauch und wo ist er noch anwendbar?

Quinton Bernhardt
quelle
50
Es ist immer noch weit verbreitet, um anzugeben und schlechte Interviewfragen zu stellen. das ist alles.
Michael Borgwardt
4
Ist das so etwas wie der Trick: a = a + b, b = ab, a = ab?
Pieter B
4
@PieterB ja das sind beide Fälle von stackoverflow.com/questions/1826159/…
jk.
Also ... Du sparst ein Register aber bezahlst mit 3 weiteren Anweisungen. Ich denke, die temporäre Version wäre sowieso schneller.
Billy ONeal
1
@MSalters Value Swap war von Anfang an kein Problem in x86 aufgrund der xchg-Anweisung. OTOH Austausch von 2 Speicherplätzen erfordern 3 xchgs :)
Netch

Antworten:

41

Bei Verwendung besteht xorswapdie Gefahr, dass dieselbe Variable wie beide Argumente an die Funktion übergeben wird, die die Variable xorauf Null setzt , da sie mit sich selbst verbunden ist und alle Bits auf Null setzt. Dies selbst würde natürlich zu unerwünschtem Verhalten führen, unabhängig vom verwendeten Algorithmus, aber das Verhalten könnte überraschend und auf den ersten Blick nicht offensichtlich sein.

Traditionell xorswapwurde es für Implementierungen auf niedriger Ebene zum Austauschen von Daten zwischen Registern verwendet. In der Praxis gibt es bessere Alternativen für den Austausch von Variablen in Registern. Zum Beispiel hat Intels x86 einen XCHGBefehl, der den Inhalt zweier Register vertauscht. Oft wird ein Compiler die Semantik einer solchen Funktion herausfinden (er tauscht den Inhalt der übergebenen Werte aus) und kann bei Bedarf seine eigenen Optimierungen vornehmen. Wenn Sie also versuchen, etwas so Triviales wie eine Swap-Funktion zu optimieren, erhalten Sie eigentlich nichts in der Praxis. Es ist am besten, die offensichtliche Methode zu verwenden, es sei denn, es gibt einen nachgewiesenen Grund, warum es unterlegen wäre, in der Problemdomäne xorswap zu sagen.

zxcdw
quelle
18
Wenn beide Werte gleich sind, erhalten Sie trotzdem das richtige Ergebnis. a: 0101 ^ 0101 = 0000; b: 0101 ^ 0000 = 0101; a: 0101 ^ 0000 = 0101;
Bobson
1
Was @ GlenH7 gesagt hat. -1-> +1.
Bobson
1
Sie scheinen immer noch die Zeile "Wenn Sie xorswap verwenden, besteht die Gefahr, dass dieselbe Variable wie beide Argumente für die Funktion angegeben wird, die die Variable auf Null setzt, da sie mit sich selbst xor'd ist, wodurch alle Bits auf Null gesetzt werden." .. Hätte das nicht entfernt werden sollen?
Chris
9
@ Chris Nein, zuerst hatte ich geschrieben, dass, wenn die Werte identisch wären a=10, b=10, und wenn Sie dies taten xorswap(a,b), dies funktionieren würde und nicht die Variablen auf Null setzen würde, was falsch ist und jetzt entfernt wird. Aber wenn Sie haben xorswap(a, a)dann auf aNull gestellt bekommen würden , die ich hatte ursprünglich gemeint war aber dumm. :)
zxcdw
3
Es ist in bestimmten Sprachen ziemlich relevant - kleine kleine Gefahren wie diese erschweren das Auffinden von Fehlern in der Zukunft, wenn es um zwei Zeiger geht, die irgendwie auf dieselbe Adresse verweisen und über 100 Zeilen lang sind, die Sie nicht sehen.
Drake Clarris
14

Der Schlüssel zur Antwort liegt in der Frage "Arbeiten mit einem Mainframe-Assembler-Programmierer" in den Tagen vor dem Compiler. Wenn Menschen sich mit Montageanleitungen und handgefertigten exakten Lösungen für ein bestimmtes Hardwareteil hinknien (das möglicherweise auf einem anderen Modell desselben Computers funktioniert oder nicht), wirkten sich Probleme wie das Timing der Festplatten und des Trommelspeichers auf die Codequalität aus geschrieben - lies Die Geschichte von Mel, wenn man das Bedürfnis hat, nostalgisch zu sein.

In früheren Zeiten waren Register und Speicher knapp, und jeder Trick, nicht um ein weiteres Byte oder ein weiteres Wort des leitenden Architekten bitten zu müssen, sparte Zeit - sowohl beim Schreiben des Codes als auch bei der Ausführung.

Diese Tage sind vorbei. Der Trick, zwei Dinge zu tauschen, ohne einen dritten zu benutzen, ist ein Trick. Sowohl Speicher als auch Register sind in modernen Computern reichlich vorhanden, und Menschen schreiben keine Assemblierung mehr. Wir haben unseren Compilern alle unsere Tricks beigebracht, und sie machen es besser als wir. Wahrscheinlich hat der Compiler etwas noch besser gemacht, als wir es getan hätten. In den meisten Fällen müssen wir Assembler manchmal aus irgendeinem Grund in ein inneres Bit einer engen Schleife schreiben ... aber nicht, um ein Register oder ein Wort aus dem Speicher zu speichern.

Es könnte wieder nützlich sein, wenn man in einem besonders eingeschränkten Mikrocontroller arbeitet, aber die Optimierung eines Swap ist dann wahrscheinlich nicht die Ursache für sein Problem - zu schlau zu sein ist wahrscheinlich ein Problem.


quelle
2
Register sind in vielen Zusammenhängen nicht wirklich so zahlreich. Selbst wenn ein Prozessor über eine ausreichende Anzahl von Registern verfügt, ist jedes in einem ISR verwendete Register ein Register, das zuvor gespeichert und anschließend wiederhergestellt werden muss. Wenn ein ISR zwanzig Zyklen dauert und alle vierzig Zyklen ausgeführt werden soll, verringert jeder zusätzliche Zyklus, der dem ISR hinzugefügt wird, die Systemleistung um fünf Prozentpunkte.
Supercat
8

Wird es funktionieren? Ja.

Solltest du es benutzen? Nein.

Diese Art der Mikrooptimierung ist sinnvoll, wenn:

  • Sie haben sich den Code angesehen, den der Compiler auf einfache Weise generiert (Zuweisung und temporärer Code), und haben festgestellt, dass der XOR-Ansatz schnelleren Code generiert

  • Sie haben Ihre App profiliert und festgestellt, dass die Kosten für den unkomplizierten Ansatz die Klarheit des Codes (und die daraus resultierenden Einsparungen bei der Wartbarkeit) überwiegen.

Bis zum ersten Punkt sollten Sie dem Compiler vertrauen, es sei denn, Sie haben diese Messung durchgeführt. Wenn die Semantik klar ist, was Sie tun möchten, kann der Compiler eine Menge Tricks anwenden, einschließlich der Neuanordnung des variablen Zugriffs, sodass der Swap überhaupt nicht benötigt wird, oder der Eingabe der Anweisungen auf Maschinenebene, die am schnellsten sind Swap für einen bestimmten Datentyp. "Tricks" wie der XOR-Tausch erschweren es dem Compiler zu sehen, was Sie versuchen, und machen es daher weniger in der Lage, solche Optimierungen anzuwenden.

Was gewinnen Sie bis zum zweiten Punkt für die zusätzliche Komplexität? Auch wenn Sie den XOR-Ansatz schneller gemessen und gefunden haben, hat dies genügend Auswirkungen, um einen weniger klaren Ansatz zu rechtfertigen? Woher weißt du das?

Schließlich sollten Sie prüfen, ob es eine Standard-Swap-Funktion für Ihre Plattform / Sprache gibt - die C ++ STL bietet beispielsweise eine Template-Swap-Funktion, die in der Regel für Ihren Compiler / Ihre Plattform optimiert ist.

im uhrzeigersinn
quelle
2

Mein Kollege berichtete, dass dies ein grundlegender Trick ist, den er während seines Studiums an einer Universität für einen Programmierer automatisierter Systeme gelehrt hat. Viele dieser Systeme sind eingebettete Systeme mit begrenzten Ressourcen, und es könnte ihnen ein freies Register fehlen, um den temporären Wert beizubehalten. In diesem Fall wird solch ein kniffliger Austausch (oder dessen Analogie mit Addieren und Subtrahieren) immer wichtiger, so dass er immer noch verwendet wird.

Es ist jedoch zu beachten, dass diese beiden Stellen nicht identisch sein können, da im letzteren Fall beide Werte effektiv auf Null gesetzt werden. Normalerweise beschränkt sich dies auf offensichtliche Fälle wie das Austauschen eines Registers und eines Speicherorts.

Mit x86 erfüllen die Befehle xchg und cmpxchg in den meisten Fällen die Anforderungen, aber RISCs werden im Allgemeinen nicht damit abgedeckt (außer Sparc).

Netch
quelle