Kara za wydajność używania clone_from_slice () zamiast copy_from_slice ()?
W Rust istnieją dwie metody aktualizowania zawartości wycinka z innego wycinka: clone_from_slice()i copy_from_slice(). Zachowanie tych dwóch funkcji nie jest zaskakujące - pierwsza klonuje i oczekuje typu do zaimplementowania Clone, a druga kopiuje i oczekuje, że typ zostanie zaimplementowany Copy.
Zaskakuje mnie jednak, że dokumentacja clone_from_slicemówi tak: „Jeśli Timplementuje Copy, może być bardziej wydajna w użyciu copy_from_slice”. Zaskakujące jest to, że tutaj powinna być różnica w wydajności. Jeśli Timplementuje Copy, .clone()to musi być równoważne z kopiowaniem bitów; jednak ponieważ kompilator wie, jaki Tjest typ , powinien być w stanie dowiedzieć się, czy może wykonać kopię bitową, nawet jeśli używam clone_from_slice.
Skąd więc bierze się nieefektywność wydajności?
Odpowiedzi
TL; DR Proszę sprawdzić źródło clone_from_slice , odwiedza wszystkie elementy plasterka i wywołuje clonekażdy z nich, podczas gdy copy_from_slice bezpośrednio kopiuje wszystkie bity z memcpy.
Jeśli T implementuje
Copy,.clone()to musi być równoważne z kopiowaniem bitów
Nawet jeśli każdy Copytyp byłby implementowany Clonedomyślnie, gdzie clonebezpośrednio używacopy ; clone_from_slicebędzie nadal przechodzić przez wycinek i kopiować podczas przechodzenia.
Ale żadna ta propozycja nie jest poprawna dla prymitywów, ale nie jest poprawna w przypadkach takich jak poniżej :
#[derive(Copy)]
struct X;
impl Clone for X {
fn clone(&self) -> Self {
//do some heavy operation or light(depends on the logic)
X
}
}
Chociaż Clonemoże być zaimplementowany przez dowolny Copytyp logiki , po prostu kopiuje bity podczas duplikowania obiektu.
Jeśli implementuje T
Copy, może być bardziej wydajne w użyciucopy_from_slice
Ważne jest tutaj, w dokumentacji jest napisane „ może być ” nie „ będzie ”, to daje takie możliwości jak
Cloneimplementacja może bezpośrednio korzystać zCopyimplementacji. W przypadku podstawowych typów, takich jak prymitywy, optymalizator może bezpośrednio użyćmemcpyzamiast przechodzenia, wtedy moglibyśmy zaakceptować tę propozycję jako błędną, ponieważ jedna nie będzie wydajna od drugiej.Cloneimplementacja może bezpośrednio korzystać zCopyimplementacji. W przypadku typów złożonych (kwestia trawersowania powyżej) ta propozycja jest poprawna. ( Edytowałem przykład z @kmdreko z nieco bardziej złożoną strukturą, sprawdź wynik z godbolt )Cloneimplementacja jest niestandardowa i jest toCopytyp, ten sprawi, że ta propozycja będzie poprawna, nawet niestandardowa implementacja jest niedroga, wtedycopydla dużych wycinków użyciememcpymoże być bardziej korzystne.