¿Penalización de rendimiento por usar clone_from_slice() en lugar de copy_from_slice()?
En Rust, hay dos métodos para actualizar el contenido de un segmento desde otro segmento: clone_from_slice()y copy_from_slice(). El comportamiento de estas dos funciones no sorprende: la primera hace una clonación y espera que el tipo la implemente Clone
, mientras que la segunda hace una copia y espera que el tipo la implemente Copy
.
Sin embargo, me sorprende que la documentación de clone_from_slice
dice esto: "If T
implements Copy
, puede ser más eficaz de usar copy_from_slice
". Es sorprendente que debería haber una diferencia de rendimiento aquí. Si T
implementa Copy
, entonces .clone()
se requiere que sea equivalente a copiar bits; sin embargo, dado que el compilador sabe qué tipo T
es, debería poder averiguar si puede hacer una copia bit a bit incluso si uso clone_from_slice
.
Entonces, ¿de dónde surge la ineficiencia en el desempeño?
Respuestas
TL; DR Verifique la fuente de clone_from_slice , está visitando todos los elementos de slice y llamando clone
a cada uno, mientras que copy_from_slice copia directamente todos los bits con memcpy
.
Si T implementa
Copy
, entonces.clone()
se requiere que sea equivalente a copiar bits
Incluso si cada Copy
tipo se implementara Clone
de forma predeterminada donde clone
se usa directamente elcopy
; clone_from_slice
seguirá recorriendo el corte y haciendo la copia mientras lo recorre.
Pero no, esta proposición es correcta para las primitivas pero no para los casos como los siguientes :
#[derive(Copy)]
struct X;
impl Clone for X {
fn clone(&self) -> Self {
//do some heavy operation or light(depends on the logic)
X
}
}
Si bien Clone
puede implementarse mediante cualquier Copy
tipo de lógica, simplemente copiará bits al duplicar un objeto.
Si T implementa
Copy
, puede ser más eficiente usarlocopy_from_slice
Lo importante está aquí, la documentación dice " puede ser " no " será ", esto trae posibilidades como
Clone
La implementación puede usar directamente laCopy
implementación. Para los tipos básicos como los primitivos, el optimizador puede usar directamentememcpy
en lugar de atravesar, entonces podemos aceptar esta proposición como incorrecta porque uno no tendrá el mismo rendimiento que el otro.Clone
La implementación puede usar directamente laCopy
implementación. Para tipos complejos (el problema de atravesar anterior) hace que esta proposición sea correcta. (He editado el ejemplo de @kmdreko con una estructura un poco más compleja, verifique el resultado de Godbolt )Clone
la implementación es personalizada y es unCopy
tipo, este hará que esta propuesta sea correcta, incluso la implementación personalizada es económica, entoncescopy
para las porciones grandes, el usomemcpy
podría ser más beneficioso.