Mit gcc 5.3 erzeugen beide Funktionen im folgenden Beispiel einen Aufruf von memmove
. Wäre es nicht angebracht, ein memcpy
zu erzeugen?
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:
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.
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
Das Durchschreiten von GDB zeigt uns:
%Vor% Warte es benutzt memmove
?! OK, lass uns weitermachen.
Was ist mit:
%Vor% OK, das bringt uns memmove
:
Was in der Assembly angezeigt wird, wenn wir -S
verwenden. Das Durchschreiten der GDB zeigt uns den Prozess:
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:
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
.
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.
Tags und Links c++ gcc assembly performance c++14