Pénalité de performance d'utiliser clone_from_slice() au lieu de copy_from_slice() ?
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_slice
indique ceci: "Si T
implements 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 T
implements Copy
, alors .clone()
doit être équivalent à copier des bits ; cependant, puisque le compilateur sait de quel type T
il 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
TL;DR Veuillez vérifier la source de clone_from_slice , il visite tous les éléments de slice et appelle clone
pour 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 Copy
type implémenterait Clone
par défaut où clone
utiliser directement lecopy
; clone_from_slice
traversera 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 Clone
peut ê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
Clone
l'implémentation peut directement utiliser l'Copy
implémentation. Pour les types de base comme les primitives, l'optimiseur peut utiliser directementmemcpy
au lieu de traverser, alors nous pourrions accepter cette proposition comme fausse car l'un ne sera pas performant alors l'autre.Clone
l'implémentation peut directement utiliser l'Copy
implé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 )Clone
l'implémentation est personnalisée et c'est unCopy
type, celui-ci rendra cette proposition correcte même si l'implémentation personnalisée est peu coûteuse, alorscopy
pour les grandes tranches, l'utilisationmemcpy
pourrait être plus bénéfique.