Ухудшение производительности при использовании clone_from_slice () вместо copy_from_slice ()?

Aug 15 2020

В Rust есть два метода обновления содержимого фрагмента из другого фрагмента: clone_from_slice()и copy_from_slice(). Поведение этих двух функций неудивительно - первая выполняет клонирование и ожидает, что тип будет реализован Clone, а вторая делает копию и ожидает, что тип будет реализован Copy.

Однако меня удивляет, что в документации для него clone_from_sliceсказано следующее: «Если оно Tреализуется Copy, оно может быть более эффективным в использовании copy_from_slice». Удивительно, что здесь должна быть разница в производительности. Если Tреализует Copy, то .clone()требуется, чтобы он был эквивалентен копированию битов; однако, поскольку компилятор знает, что это за тип T, он должен выяснить, может ли он выполнить побитовое копирование, даже если я использую clone_from_slice.

Так откуда же возникает неэффективность производительности?

Ответы

4 ÖmerErden Aug 15 2020 at 21:14

TL; DR Пожалуйста, проверьте источник clone_from_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может быть более выгодным.