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_slice
dica questo: "Se T
implements Copy
, può essere più performante da usare copy_from_slice
." È sorprendente che qui dovrebbe esserci una differenza di prestazioni. Se T
implementa 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 clone
ciascuno, 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 Copy
tipo implementasse Clone
per impostazione predefinita dove clone
usa direttamente ilcopy
; clone_from_slice
attraverserà 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 Clone
possa essere implementato da qualsiasi Copy
tipo 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
Clone
l'implementazione può utilizzare direttamente l'Copy
implementazione. Per i tipi di base come i primitivi, l'ottimizzatore può utilizzare direttamentememcpy
invece di attraversare, quindi potremmo accettare questa proposizione come sbagliata perché uno non sarà performante dell'altro.Clone
l'implementazione può utilizzare direttamente l'Copy
implementazione. 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 )Clone
l'implementazione è personalizzata ed è unCopy
tipo, questo renderà questa proposta corretta anche se l'implementazione personalizzata è poco costosa, quindicopy
per le fette di grandi dimensioni l'utilizzomemcpy
potrebbe essere più vantaggioso.