Beispiele, wenn ein bitweises swap () eine schlechte Idee ist?

7

Sie dürfen Objektzeiger nicht als Zeiger auf rohe Binärdaten in OOP-Sprachen, einschließlich C ++, behandeln. Objekte sind "mehr als" ihre Repräsentation.

So ist beispielsweise swap ing zwei Objekte durch Austauschen ihrer Bytes falsch:

%Vor%

Die einzige Situation jedoch, in der ich mir vorstellen kann, dass diese Abkürzung ein Problem verursacht ist, wenn ein Objekt einen Zeiger auf sich selbst enthält, den ich selten (nie?) in der Praxis gesehen habe; es kann aber auch andere Szenarien geben.

Was sind einige reale (reale) Beispiele dafür, wann ein korrektes swap brechen würde, wenn Sie einen bitweisen Swap durchgeführt hätten?
Ich kann leicht mit erfundenen Beispielen mit Selbstzeigern aufwarten, aber ich kann mir keine echten vorstellen.

    
Mehrdad 24.07.2012, 19:49
quelle

5 Antworten

8

Ich werde argumentieren, dass dies fast immer eine schlechte Idee ist, außer in dem speziellen Fall, in dem Profiling durchgeführt wurde und eine offensichtlichere und klarere Implementierung von swap Leistungsprobleme hat. Selbst in diesem Fall würde ich nur mit dieser Art von Ansatz für geradlinige Strukturen ohne Vererbung gehen, niemals für irgendeine Art von Klasse. Man weiß nie, wann eine Vererbung hinzugefügt wird, die möglicherweise die ganze Sache durchbricht (möglicherweise auch auf wirklich heimtückische Weise).

Wenn Sie eine schnelle Swap-Implementierung wünschen, ist es vielleicht eine bessere Wahl (falls zutreffend), die Klasse zu pimplieren und dann die Implementierung einfach auszutauschen (dies setzt wiederum voraus, dass es keine Backpointer für den Besitzer gibt, die aber leicht enthalten sind) zur Klasse und impl eher als externe Faktoren).

EDIT: Mögliche Probleme mit diesem Ansatz:

  • Zeiger zurück auf sich selbst (direkt oder indirekt)
  • Wenn die Klasse ein Objekt enthält, für das eine direkte Byte-Kopie bedeutungslos ist (diese Definition wird effektiv rekursiv verwendet) oder für die das Kopieren normalerweise deaktiviert ist
  • Wenn die Klasse eine Art Locking zum Kopieren benötigt
  • Es ist einfach , hier versehentlich zwei verschiedene Typen zu übergeben (es genügt eine Zwischenfunktion, um eine abgeleitete Klasse implizit so aussehen zu lassen wie die Eltern), und dann tauscht man vptrs (OUCH!)
Mark B 24.07.2012, 20:18
quelle
13

Hier geht es nicht speziell um swap , aber ein Beispiel, das zeigt, dass Low-Level-Optimierungen den Aufwand vielleicht nicht wert sind. Der Compiler findet es oft trotzdem heraus.

Natürlich ist dies mein Lieblingsbeispiel, bei dem der Compiler außergewöhnlich glücklich ist, aber trotzdem sollten wir nicht davon ausgehen, dass Compiler dumm sind und dass wir den generierten Code mit ein paar einfachen Tricks leicht verbessern können.

Mein Testcode ist - konstruiere eine std :: string und kopiere sie.

%Vor%

Der erste Konstruktor sieht so aus

%Vor%

Der generierte Code ist

%Vor%

Hier enthält traits_type::copy einen Aufruf von memcpy , der zu einer einzelnen Registerkopie der ganzen Zeichenfolge optimiert ist (sorgfältig passend ausgewählt). Der Compiler transformiert außerdem einen Aufruf in strlen in eine Kompilierzeit 8 .

Dann kopieren wir es in eine neue Zeichenfolge. Der Kopierkonstruktor sieht so aus

%Vor%

und ergibt nur 4 Maschinenanweisungen:

%Vor%

Beachten Sie, dass sich das Optimierungsprogramm daran erinnert, dass sich die char noch im Register rdx befinden und dass die Stringlänge gleich sein muss, 8 .

Nachdem ich solche Dinge gesehen habe, vertraue ich gerne meinem Compiler und vermeide es, den Code mit etwas Fummelei zu verbessern. Es hilft nicht, es sei denn, das Profiling findet einen unerwarteten Engpass.

(mit MSVC 10 und meiner std :: string-Implementierung)

    
Bo Persson 24.07.2012 21:05
quelle
3

Warum werden "Selbstzeiger" erfunden?

%Vor%

Dieser Typ enthält einen Puffer und eine aktuelle Position im Puffer.

Oder vielleicht haben Sie schon von Iostreams gehört:

%Vor%

Wie bereits erwähnt, die kleine String-Optimierung:

%Vor%

Dies hat auch einen Selbstzeiger. Wenn Sie zwei kleine Zeichenfolgen konstruieren und diese dann austauschen, entscheiden die Destruktoren, dass die Zeichenfolge "nicht lokal" ist und versuchen, den Speicher zu löschen:

%Vor%

Valgrind sagt:

%Vor%

Dies zeigt, dass es Typen wie std::streambuf und std::string betrifft, kaum erfundene oder esoterische Beispiele.

Grundsätzlich ist bad_swap nie eine gute Idee, wenn die Typen trivial kopierbar sind, dann ist der Standard std::swap optimal (von deinem Compiler wird er nicht auf memcpy optimiert, dann bekommst du ihn ein besserer Compiler) und wenn sie nicht trivial kopierbar sind, ist es eine großartige Möglichkeit, Mr. Undefined Behavior und seinen Freund Mr. Serious Bug zu treffen.

    
Jonathan Wakely 24.07.2012 20:39
quelle
2

Neben den in anderen Antworten erwähnten Beispielen (insbesondere Objekten, die Zeiger auf Teile von sich selbst und Objekte enthalten, die gesperrt werden müssen), könnten auch Zeiger auf das Objekt von einer externen Datenstruktur verwaltet werden, die entsprechend aktualisiert werden muss ( Bitte beachten Sie, dass das Beispiel etwas erfunden ist, um nicht zu übertrieben zu sein (und vielleicht fehlerhaft, weil es nicht getestet wurde):

%Vor%

offensichtlich würde so etwas kaputt gehen, wenn Objekte durch memcpy getauscht werden. Natürlich sind reale Beispiele dafür typischerweise etwas komplexer, aber der Punkt sollte klar sein.

Neben den Beispielen denke ich, dass das Kopieren (oder Swapping) nicht trivial kopierbarer Objekte wie dieses ein undefiniertes Verhalten des Standards ist (könnte das später überprüfen). In diesem Fall würde es überhaupt keine Garantie dafür geben, dass dieser Code mit komplexeren Objekten arbeitet.

    
Grizzly 24.07.2012 20:53
quelle
1

Einige nicht bereits erwähnt:

  • Der Tausch kann Nebeneffekte haben, z. B. müssen Sie die Zeiger externer Elemente aktualisieren, um auf den neuen Ort zu zeigen, oder hörende Objekte informieren, dass sich der Inhalt des Objekts geändert hat.
  • Der Austausch von zwei Elementen, die relative Adressen verwenden, würde Probleme verursachen
josefx 24.07.2012 20:40
quelle

Tags und Links