copy_from_slice()の代わりにclone_from_slice()を使用するとパフォーマンスが低下しますか?
Rustでは、別のスライスからスライスのコンテンツを更新する方法が2つあります。clone_from_slice()とcopy_from_slice()。これら2つの関数の動作は驚くべきものではありません。最初の関数はクローンを実行して型が実装されることを期待Clone
し、2番目の関数はコピーを実行して型が実装することを期待しますCopy
。
ただし、のドキュメントにclone_from_slice
次のように記載されていることに驚いています。「をT
実装するとCopy
、使用するパフォーマンスが向上する可能性がありますcopy_from_slice
。」ここでパフォーマンスに違いがあるのは驚くべきことです。をT
実装する場合Copy
、.clone()
ビットのコピーと同等である必要があります。ただし、コンパイラT
は型が何であるかを知っているので、を使用してもビット単位のコピーを実行できるかどうかを判断できるはずclone_from_slice
です。
では、パフォーマンスの非効率性はどこから発生するのでしょうか。
回答
TL; DR clone_from_sliceのソースを確認してください。これは、sliceのすべての要素にアクセスしてclone
それぞれを呼び出していますが、copy_from_sliceはすべてのビットをmemcpy
。で直接コピーします。
Tが実装する場合
Copy
、.clone()
ビットのコピーと同等である必要があります
すべてのCopy
タイプがClone
デフォルトで実装される場合でもclone
、copy
;を直接使用します。clone_from_slice
スライスをトラバースし、トラバース中にコピーを実行します。
しかし、この命題はプリミティブには正しくありませんが、以下のような場合には正しくありません:
#[derive(Copy)]
struct X;
impl Clone for X {
fn clone(&self) -> Self {
//do some heavy operation or light(depends on the logic)
X
}
}
しながら、Clone
任意の論理によって実施することができるCopy
種類のオブジェクトを複製する際に、単にビットをコピーします。
Tが実装する場合
Copy
、使用する方がパフォーマンスが高くなる可能性がありますcopy_from_slice
重要なことは、ここにある、ドキュメントが「言うことはできない」「それはなります」、これは次のような可能性をもたらします
Clone
実装は実装を直接使用できますCopy
。プリミティブのような基本的なタイプの場合、オプティマイザーmemcpy
はトラバースする代わりに直接使用する可能性があり、一方が他方よりもパフォーマンスが高くないため、この提案を間違っていると受け入れる可能性があります。Clone
実装は実装を直接使用できますCopy
。複雑なタイプ(上記のトラバースの問題)の場合、この命題は正しくなります。(@kmdrekoの例をもう少し複雑な構造で編集しました。godboltの結果を確認してください)Clone
実装はカスタムであり、Copy
タイプです。これにより、カスタム実装が安価であっても、この提案が正しくcopy
なり、大きなスライスを使用memcpy
する方が有益な場合があります。