Rustで高速にsliceの一部をCloneする

published: 2022/6/4 update: 2022/6/4

Table of Contents

TL;DR

Rustで配列をコピーしたいとき、実際問題何をするのが早いのかという話です。 結論だけ書いておくと、clone_from_sliceを使えばいいはずです。

clone_from_sliceの実装を読む

clone_from_slice

実装は以下です。spec_clone_fromを読んでいるだけです。

pub fn clone_from_slice(&mut self, src: &[T])
where
    T: Clone,
{
    self.spec_clone_from(src);
}

spec_clone_from

spec_clone_fromが何者なのかという話ですが、Tのオーバロード的なことをしている関数です。オーバロードっぽいことはtraitを使うことで実現できます(参考)。

T: Cloneの場合は普通にcloneして、T: Copyの場合はcopy_from_sliceを呼びます。

source

trait CloneFromSpec<T> {
    fn spec_clone_from(&mut self, src: &[T]);
}

impl<T> CloneFromSpec<T> for [T]
where
    T: Clone,
{
    #[track_caller]
    default fn spec_clone_from(&mut self, src: &[T]) {
        assert!(
            self.len() == src.len(),
            "destination and source slices have different lengths"
        );
        // NOTE: We need to explicitly slice them to the same length
        // to make it easier for the optimizer to elide bounds checking.
        // But since it can't be relied on we also have an explicit specialization for T: Copy.
        let len = self.len();
        let src = &src[..len];
        for i in 0..len {
            self[i].clone_from(&src[i]);
        }
    }
}

impl<T> CloneFromSpec<T> for [T]
where
    T: Copy,
{
    #[track_caller]
    fn spec_clone_from(&mut self, src: &[T]) {
        self.copy_from_slice(src);
    }
}

copy_from_slice

ptr::copy_nonoverlappingを呼びます。そのためのsafetyの確認も行われています。ptr::copy_nonoverlappingはだいたいmemcpyなので、Copy traitが必要です。

source

pub fn copy_from_slice(&mut self, src: &[T])
where
    T: Copy,
{
    // The panic code path was put into a cold function to not bloat the
    // call site.
    #[inline(never)]
    #[cold]
    #[track_caller]
    fn len_mismatch_fail(dst_len: usize, src_len: usize) -> ! {
        panic!(
            "source slice length ({}) does not match destination slice length ({})",
            src_len, dst_len,
        );
    }

    if self.len() != src.len() {
        len_mismatch_fail(self.len(), src.len());
    }

    // SAFETY: `self` is valid for `self.len()` elements by definition, and `src` was
    // checked to have the same length. The slices cannot overlap because
    // mutable references are exclusive.
    unsafe {
        ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len());
    }
}

結論

ということで、clone_from_sliceを呼べばよしなにやってくれるらしいです。 明示的にやりたい場合は、ptr::copy_nonoverlappingでもいいですが、unsafeは可能なら呼びたくないので、copy_from_sliceを呼ぶのがいいと思います。

記事に間違い等ありましたら、お気軽に以下までご連絡ください

E-mail: illumination.k.27|gmail.com ("|" replaced to "@")

Twitter: @illuminationK

当HPを応援してくれる方は下のリンクからお布施をいただけると非常に励みになります。

Ofuse

Other Articles

Site Map

Table of Contents

    TL;DR

    clone_from_sliceの実装を読む

      clone_from_slice

      spec_clone_from

      copy_from_slice

    結論


当HPを応援してくれる方は下のリンクからお布施をいただけると非常に励みになります。

Ofuse
Privacy Policy

Copyright © illumination-k 2020-2022.