Leistungseinbußen bei der Verwendung von clone_from_slice() anstelle von copy_from_slice()?
In Rust gibt es zwei Methoden, um den Inhalt eines Slice von einem anderen Slice zu aktualisieren: clone_from_slice()und copy_from_slice(). Das Verhalten dieser beiden Funktionen ist nicht überraschend – die erste erstellt einen Klon und erwartet, dass der Typ implementiert Clone
wird, während die zweite eine Kopie erstellt und erwartet, dass der Typ implementiert wird Copy
.
Es überrascht mich jedoch, dass die Dokumentation für clone_from_slice
Folgendes sagt: "Wenn T
implementiert Copy
, kann die Verwendung von performanter sein copy_from_slice
." Dass es hier einen Leistungsunterschied geben soll, verwundert. Wenn T
implementiert Copy
, muss then .clone()
dem Kopieren von Bits entsprechen; Da der Compiler jedoch weiß, welcher Typ T
ist, sollte er in der Lage sein, herauszufinden, ob er bitweise kopieren kann, selbst wenn ich clone_from_slice
.
Woher kommt also die Leistungsineffizienz?
Antworten
TL;DR Bitte überprüfen Sie die Quelle von clone_from_slice , es besucht alle Elemente von Slice und ruft clone
für jedes auf, während copy_from_slice direkt alle Bits mit kopiert memcpy
.
Wenn T implementiert
Copy
, dann.clone()
muss es dem Kopieren von Bits entsprechen
Auch wenn jeder Copy
Typ Clone
standardmäßig implementieren würde, wo clone
direkt dascopy
; clone_from_slice
wird immer noch das Slice durchlaufen und die Kopie während des Traversierens erstellen.
Aber nein, dieser Satz ist für Primitive richtig, aber nicht für die folgenden Fälle :
#[derive(Copy)]
struct X;
impl Clone for X {
fn clone(&self) -> Self {
//do some heavy operation or light(depends on the logic)
X
}
}
Während Clone
beliebige Logiktypen implementiert werden können Copy
, werden beim Duplizieren eines Objekts einfach Bits kopiert.
Wenn T implementiert
Copy
, kann die Verwendung performanter seincopy_from_slice
Wichtig ist hier drin, die Dokumentation sagt " es kann sein " nicht " es wird sein ", das bringt Möglichkeiten wie
Clone
Die Implementierung kann die Implementierung direkt verwendenCopy
. Für die Grundtypen wie Primitive kann der Optimierer direkt verwenden,memcpy
anstatt zu traversieren, dann könnten wir diesen Vorschlag als falsch akzeptieren, da einer nicht leistungsfähiger als der andere ist.Clone
Die Implementierung kann die Implementierung direkt verwendenCopy
. Für komplexe Typen (das Traversierungsproblem oben) macht diese Aussage richtig. (Ich habe das Beispiel von @kmdreko mit einer etwas komplexeren Struktur bearbeitet, bitte überprüfen Sie das Ergebnis von godbolt )Clone
Die Implementierung ist benutzerdefiniert und es ist einCopy
Typ, dieser wird diesen Vorschlag korrekt machen, auch wenn die kundenspezifische Implementierung kostengünstig ist, dann könntecopy
die Verwendung großer Slicesmemcpy
vorteilhafter sein.