Pena de desempenho de usar clone_from_slice () em vez de copy_from_slice ()?

Aug 15 2020

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_slicediga o seguinte: "Se Timplementa Copy, pode ser mais eficiente de usar copy_from_slice." É surpreendente que haja uma diferença de desempenho aqui. Se Timplementa 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

4 ÖmerErden Aug 15 2020 at 21:14

TL;DR Verifique a fonte de clone_from_slice , ele está visitando todos os elementos da fatia e chamando clonecada 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 Copytipo seja implementado Clonepor padrão onde cloneusar diretamente ocopy ; clone_from_sliceainda 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 Clonepossa 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

  • Cloneimplementação pode usar diretamente a Copyimplementação. Para os tipos básicos como primitivos, o otimizador pode usar diretamente memcpyem vez de percorrer, então podemos aceitar essa proposição como errada porque um não terá desempenho e outro.

  • Cloneimplementação pode usar diretamente a Copyimplementaçã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 )

  • Cloneimplementação é personalizada e é um Copytipo, este fará esta proposição correta, mesmo a implementação personalizada é barata, então, copypara grandes fatias, o uso memcpypode ser mais benéfico.