Pénalité de performance d'utiliser clone_from_slice() au lieu de copy_from_slice() ?

Aug 15 2020

Dans Rust, il existe deux méthodes pour mettre à jour le contenu d'une tranche à partir d'une autre tranche : clone_from_slice()et copy_from_slice(). Le comportement de ces deux fonctions n'est pas surprenant : la première fait un clone et s'attend à ce que le type soit implémenté Clone, tandis que la seconde fait une copie et s'attend à ce que le type soit implémenté Copy.

Cependant, cela me surprend que la documentation de clone_from_sliceindique ceci: "Si Timplements Copy, il peut être plus performant d'utiliser copy_from_slice." Il est surprenant qu'il y ait une différence de performances ici. Si Timplements Copy, alors .clone()doit être équivalent à copier des bits ; cependant, puisque le compilateur sait de quel type Til s'agit, il devrait être capable de déterminer s'il peut faire une copie au niveau du bit même si j'utilise clone_from_slice.

Alors, d'où vient l'inefficacité des performances ?

Réponses

4 ÖmerErden Aug 15 2020 at 21:14

TL;DR Veuillez vérifier la source de clone_from_slice , il visite tous les éléments de slice et appelle clonepour chacun, tandis que copy_from_slice copie directement tous les bits avec memcpy.


Si T implémente Copy, alors .clone()doit être équivalent à copier des bits

Même si chaque Copytype implémenterait Clonepar défaut où cloneutiliser directement lecopy ; clone_from_slicetraversera toujours la tranche et fera la copie tout en traversant.

Mais non cette proposition est correcte pour les primitives mais pas correcte pour les cas comme ci-dessous :

#[derive(Copy)]
struct X;

impl Clone for X {
    fn clone(&self) -> Self {
        //do some heavy operation or light(depends on the logic)

        X
    }
}

Alors que Clonepeut être implémenté par n'importe quel type de logique Copy, il copiera simplement des bits lors de la duplication d'un objet.

Si T implémente Copy, il peut être plus performant d'utilisercopy_from_slice

La chose importante est ici, la documentation dit " ça peut être " pas " ce sera ", cela apporte des possibilités comme

  • Clonel'implémentation peut directement utiliser l' Copyimplémentation. Pour les types de base comme les primitives, l'optimiseur peut utiliser directement memcpyau lieu de traverser, alors nous pourrions accepter cette proposition comme fausse car l'un ne sera pas performant alors l'autre.

  • Clonel'implémentation peut directement utiliser l' Copyimplémentation. Pour les types complexes (le problème de traversée ci-dessus) rend cette proposition correcte. (J'ai modifié l' exemple de @kmdreko avec une structure un peu plus complexe, veuillez vérifier le résultat de godbolt )

  • Clonel'implémentation est personnalisée et c'est un Copytype, celui-ci rendra cette proposition correcte même si l'implémentation personnalisée est peu coûteuse, alors copypour les grandes tranches, l'utilisation memcpypourrait être plus bénéfique.