Penalità delle prestazioni dell'utilizzo di clone_from_slice() invece di copy_from_slice()?
In Rust, ci sono due metodi per aggiornare il contenuto di una slice da un'altra slice: clone_from_slice()e copy_from_slice(). Il comportamento di queste due funzioni non è sorprendente: la prima esegue un clone e si aspetta che il tipo venga implementato Clone, mentre la seconda esegue una copia e si aspetta che il tipo venga implementato Copy.
Tuttavia, mi sorprende che la documentazione per clone_from_slicedica questo: "Se Timplements Copy, può essere più performante da usare copy_from_slice." È sorprendente che qui dovrebbe esserci una differenza di prestazioni. Se Timplementa Copy, allora .clone()deve essere equivalente alla copia dei bit; tuttavia, poiché il compilatore sa che tipo Tè, dovrebbe essere in grado di capire se può eseguire una copia bit per bit anche se utilizzo clone_from_slice.
Quindi da dove deriva l'inefficienza delle prestazioni?
Risposte
TL; DR Controlla la fonte di clone_from_slice , sta visitando tutti gli elementi di slice e chiamando cloneciascuno, mentre copy_from_slice copia direttamente tutti i bit con memcpy.
Se T implementa
Copy, allora.clone()deve essere equivalente alla copia dei bit
Anche se ogni Copytipo implementasse Cloneper impostazione predefinita dove cloneusa direttamente ilcopy ; clone_from_sliceattraverserà comunque la sezione ed eseguirà la copia durante l'attraversamento.
Ma no questa proposizione è corretta per le primitive ma non corretta per i casi come di seguito :
#[derive(Copy)]
struct X;
impl Clone for X {
fn clone(&self) -> Self {
//do some heavy operation or light(depends on the logic)
X
}
}
Sebbene Clonepossa essere implementato da qualsiasi Copytipo di logica, copierà semplicemente i bit durante la duplicazione di un oggetto.
Se T implementa
Copy, può essere più performante da usarecopy_from_slice
La cosa importante è qui, la documentazione dice " può essere " non " sarà ", questo porta possibilità come
Clonel'implementazione può utilizzare direttamente l'Copyimplementazione. Per i tipi di base come i primitivi, l'ottimizzatore può utilizzare direttamentememcpyinvece di attraversare, quindi potremmo accettare questa proposizione come sbagliata perché uno non sarà performante dell'altro.Clonel'implementazione può utilizzare direttamente l'Copyimplementazione. Per i tipi complessi (il problema di attraversamento sopra) rende corretta questa proposizione. (Ho modificato l' esempio da @kmdreko con una struttura un po' più complessa, controlla il risultato da godbolt )Clonel'implementazione è personalizzata ed è unCopytipo, questo renderà questa proposta corretta anche se l'implementazione personalizzata è poco costosa, quindicopyper le fette di grandi dimensioni l'utilizzomemcpypotrebbe essere più vantaggioso.