Leistungseinbußen bei der Verwendung von clone_from_slice() anstelle von copy_from_slice()?

Aug 15 2020

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 Clonewird, 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_sliceFolgendes sagt: "Wenn Timplementiert Copy, kann die Verwendung von performanter sein copy_from_slice." Dass es hier einen Leistungsunterschied geben soll, verwundert. Wenn Timplementiert Copy, muss then .clone()dem Kopieren von Bits entsprechen; Da der Compiler jedoch weiß, welcher Typ Tist, sollte er in der Lage sein, herauszufinden, ob er bitweise kopieren kann, selbst wenn ich clone_from_slice.

Woher kommt also die Leistungsineffizienz?

Antworten

4 ÖmerErden Aug 15 2020 at 21:14

TL;DR Bitte überprüfen Sie die Quelle von clone_from_slice , es besucht alle Elemente von Slice und ruft clonefü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 CopyTyp Clonestandardmäßig implementieren würde, wo clonedirekt dascopy ; clone_from_slicewird 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 Clonebeliebige 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

  • CloneDie Implementierung kann die Implementierung direkt verwenden Copy. Für die Grundtypen wie Primitive kann der Optimierer direkt verwenden, memcpyanstatt zu traversieren, dann könnten wir diesen Vorschlag als falsch akzeptieren, da einer nicht leistungsfähiger als der andere ist.

  • CloneDie Implementierung kann die Implementierung direkt verwenden Copy. 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 )

  • CloneDie Implementierung ist benutzerdefiniert und es ist ein CopyTyp, dieser wird diesen Vorschlag korrekt machen, auch wenn die kundenspezifische Implementierung kostengünstig ist, dann könnte copydie Verwendung großer Slices memcpyvorteilhafter sein.