Ухудшение производительности при использовании clone_from_slice () вместо copy_from_slice ()?
В Rust есть два метода обновления содержимого фрагмента из другого фрагмента: clone_from_slice()и copy_from_slice(). Поведение этих двух функций неудивительно - первая выполняет клонирование и ожидает, что тип будет реализован Clone
, а вторая делает копию и ожидает, что тип будет реализован Copy
.
Однако меня удивляет, что в документации для него clone_from_slice
сказано следующее: «Если оно T
реализуется Copy
, оно может быть более эффективным в использовании copy_from_slice
». Удивительно, что здесь должна быть разница в производительности. Если T
реализует Copy
, то .clone()
требуется, чтобы он был эквивалентен копированию битов; однако, поскольку компилятор знает, что это за тип T
, он должен выяснить, может ли он выполнить побитовое копирование, даже если я использую clone_from_slice
.
Так откуда же возникает неэффективность производительности?
Ответы
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
может быть более выгодным.