Warum erzeugt gcc anstelle von memcpy ein memove zum Kopieren eines std :: vector?

8

Mit gcc 5.3 erzeugen beide Funktionen im folgenden Beispiel einen Aufruf von memmove . Wäre es nicht angebracht, ein memcpy zu erzeugen?

%Vor%

Beispiel für godbolt .

    
Praxeolitic 12.05.2016, 00:09
quelle

3 Antworten

9

Ich habe versucht, diesen Code mit g ++ 6.1.0 zu kompilieren. Ich bin über die Details nicht ganz sicher, aber ich denke, dass der Aufruf memmove nicht direkt vom Compiler generiert wird; Es ist vielmehr der Code, der <vector> implementiert.

Wenn ich den Code mit

vorbearbeite %Vor%

Ich sehe zwei Aufrufe von __builtin_memmove , die beide aus .../include/c++/6.1.0/bits/stl_algobase.h stammen. Wenn ich diese Header-Datei anschaue, sehe ich diesen Kommentar:

%Vor%

Ich denke, was passiert, ist, dass der zum Kopieren des Vektors aufgerufene Code allgemeiner auf Kopien anwendbar ist, die sich überlappen können (wie ein Aufruf von std::move (?)).

(Ich habe nicht bestätigt, dass die memmove -Aufrufe, die in der Assembly-Liste erscheinen, den __builtin_memmove -Aufrufen in stl_algobase.h entsprechen. Ich lade jemanden dazu ein, diesen Punkt zu verfolgen.)

Abhängig von der Implementierung kann memmove() relativ zu memcpy() einige Gemeinkosten haben, aber der Unterschied ist gering. Es war wahrscheinlich nicht sinnvoll, Sonderfälle für Kopien zu erstellen, die sich nicht überschneiden können.

    
Keith Thompson 12.05.2016, 00:58
quelle
8

TL; DR GCC optimiert den Aufruf von memmove in std::copy nicht. Wenn zwei C-artige Arrays verwendet werden, ist dies der Fall. Wenn &v2[0] durch *v2.data() ersetzt wird, kann es in memcpy optimiert werden.

Ihr Beispiel ist ziemlich laut, also lasst es uns streichen:

%Vor%

Ich habe die Variablen absichtlich in den Dateibereich gestellt, um zu verhindern, dass sie optimiert werden, ohne sich mit volatile semantisch befassen zu müssen.

Zuerst versuchen wir es:

%Vor%

Bei -O3 -fdump-tree-optimized wird dies zu

%Vor%

Das Durchschreiten von GDB zeigt uns:

%Vor%

Warte es benutzt memmove ?! OK, lass uns weitermachen.

Was ist mit:

%Vor%

OK, das bringt uns memmove :

%Vor%

Was in der Assembly angezeigt wird, wenn wir -S verwenden. Das Durchschreiten der GDB zeigt uns den Prozess:

%Vor%

Ah, ich verstehe. Es verwendet eine optimierte memcpy -Routine, die von der C-Bibliothek bereitgestellt wird. Aber warte mal, das macht keinen Sinn. memmove und memcpy sind zwei verschiedene Dinge!

Mit Blick auf die Quellcode für diese Routine sehen wir kleine Überprüfungen durchgestreut:

%Vor%

Die GDB bestätigt, dass sie sie als memmove behandelt:

%Vor%

Aber wenn wir &v2[0] durch *v2.data() ersetzen, wird die memmove des GLIBC nicht aufgerufen. Also, was ist los?

Well v2[0] und v2.begin() geben Iteratoren zurück, während v2.data() einen direkten Zeiger auf den Speicher zurückgibt. Ich denke, dies verhindert aus irgendeinem Grund, dass GCC das memmove in ein memcpy .

optimiert     
user6323422 12.05.2016 04:37
quelle
5

Der Grund dafür, dass der Implementierer memmove über memcpy verwendet, könnte in diesem Fall fehlerhaft sein.

memmove unterscheidet sich von memcpy dadurch, dass sich die Speicherbereiche in memmove überlappen können (und daher konzeptionell etwas weniger effizient ist).

memcpy hat die Einschränkung, dass die beiden Speicherbereiche nicht überlappen dürfen.

Im Fall des Kopierkonstruktors des Vektors überschneiden sich die Speicherbereiche niemals, so dass argumentiert werden könnte, dass memcpy die bessere Wahl wäre.

    
Richard Hodges 12.05.2016 00:22
quelle

Tags und Links