Pena de desempenho de usar clone_from_slice () em vez de copy_from_slice ()?
No Rust, existem dois métodos para atualizar o conteúdo de uma fatia de outra fatia: clone_from_slice()e copy_from_slice(). O comportamento dessas duas funções não surpreende — a primeira faz um clone e espera que o tipo seja implementado Clone
, enquanto a segunda faz uma cópia e espera que o tipo seja implementado Copy
.
No entanto, me surpreende que a documentação de clone_from_slice
diga o seguinte: "Se T
implementa Copy
, pode ser mais eficiente de usar copy_from_slice
." É surpreendente que haja uma diferença de desempenho aqui. Se T
implementa Copy
, então .clone()
é necessário ser equivalente a copiar bits; no entanto, como o compilador sabe qual T
é o tipo, ele deve ser capaz de descobrir se pode fazer uma cópia bit a bit, mesmo se eu usar clone_from_slice
.
Então, de onde surge a ineficiência de desempenho?
Respostas
TL;DR Verifique a fonte de clone_from_slice , ele está visitando todos os elementos da fatia e chamando clone
cada um, enquanto copy_from_slice copia diretamente todos os bits com memcpy
.
Se T implementa
Copy
, então.clone()
é necessário ser equivalente a copiar bits
Mesmo que cada Copy
tipo seja implementado Clone
por padrão onde clone
usar diretamente ocopy
; clone_from_slice
ainda percorrerá a fatia e fará a cópia durante a travessia.
Mas não, esta proposição está correta para primitivos, mas não correta para os casos abaixo :
#[derive(Copy)]
struct X;
impl Clone for X {
fn clone(&self) -> Self {
//do some heavy operation or light(depends on the logic)
X
}
}
Embora Clone
possa ser implementado por qualquer tipo de lógica Copy
, ele simplesmente copiará bits ao duplicar um objeto.
Se T implementa
Copy
, pode ser mais eficiente usarcopy_from_slice
Importante está aqui dentro, a documentação diz " pode ser " não " vai ser ", isso traz possibilidades como
Clone
implementação pode usar diretamente aCopy
implementação. Para os tipos básicos como primitivos, o otimizador pode usar diretamentememcpy
em vez de percorrer, então podemos aceitar essa proposição como errada porque um não terá desempenho e outro.Clone
implementação pode usar diretamente aCopy
implementação. Para tipos complexos (o problema de passagem acima) torna esta proposição correta. (Editei o exemplo de @kmdreko com uma estrutura um pouco mais complexa, verifique o resultado de godbolt )Clone
implementação é personalizada e é umCopy
tipo, este fará esta proposição correta, mesmo a implementação personalizada é barata, então,copy
para grandes fatias, o usomemcpy
pode ser mais benéfico.