Penalità delle prestazioni dell'utilizzo di clone_from_slice() invece di copy_from_slice()?

Aug 15 2020

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

4 ÖmerErden Aug 15 2020 at 21:14

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 direttamente memcpyinvece 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 è un Copytipo, questo renderà questa proposta corretta anche se l'implementazione personalizzata è poco costosa, quindi copyper le fette di grandi dimensioni l'utilizzo memcpypotrebbe essere più vantaggioso.