Я наткнулся на статью Нареша Джоши о копировании и клонировании и был удивлён ситуацией с производительностью. У клонирования есть проблемы с финальными полями. А учитывая тот факт, что интерфейс Cloneable не предоставляет метод clone
, то для вызова clone
вам необходимо будет знать конкретный тип класса.
Вы можете написать такой код:
((Cloneable)o).clone(); // не работает
Если интерфейс Cloneable
сломан, то у механизма клонирования могут быть некоторые преимущества. При копировании памяти он может оказаться эффективнее, чем копирование поля за полем. Это подчёркивает Джош Блох, автор Effective Java:
Даг Ли пошёл ещё дальше. Он сказал мне, что теперь клонирует только при копировании массивов. Вам следует использовать клонирование копирования массивов, потому что в целом это самый быстрый способ. Но у Дага типы больше не реализуют Cloneable
. Он с ним завязал. И я не считаю это необоснованным.
Но это было в 2002-м, разве ситуация не изменилась? Со времён Java 6 у нас есть Arrays.copyOf
, что насчёт него? Какова производительность копирования объекта?
Есть только один способ выяснить: прогнать бенчмарки.
TL;DR
Клонирование работает быстрее при копировании массива, это заметно на маленьких массивах.- Arrays.copyOf и clone примерно одинаково работают
- Клонирование работает медленнее для маленьких объектов, меньше восьми полей, но в любом случае быстрее.
- При клонировании не работает escape analysis, и потенциально оно может помешать применению других оптимизаций.
Массивы
[UPD]Andrei Paguin указал в комментариях что с бенчмарком есть проблема.
замените “size” на “original.length” в бенчмарке Arrays.copyOf().
И тут я понял, что да… это объясняет почему jit может понять что мы копируем ровно такую же длину. Поэтому я изменил выводы и статью
Давайте быстро рассмотрим clone
и Arrays.copyOf
применительно к массивам.
Бенчмарк int array
выглядит так:
@Benchmark
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public int[] testCopy() {
return Arrays.copyOf(original, original.length); // здесь было size
}
@Benchmark
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
public int[] testClone() {
return original.clone();
}
Мы создали массив из случайных числовых значений, затем выполнили clone
или Arrays.copyOf
. Обратите внимание: мы вернули результат копирования, чтобы гарантировать, что код будет выполнен. В главе про escape analysis мы увидим, как невозвращение массива может радикально повлиять на бенчмарк.
Наряду с int array
есть версия для byte array
, long array
и Object array
. Я использую флаг DONT_INLINE
, чтобы при необходимости легче было анализировать сгенерированный asm.
mvn clean install
java -jar target/benchmark.jar -bm avgt -tu ns -rf csv
- здесь будут обновленные результаты —
Объекты
Теперь разберёмся с клонированием объектов с 4, 8, 16 и 32 полями. Бенчмарки ищут объекты с 4 полями:
Комментарии (5)
apangin
27.07.2017 16:53+1Как видите, clone по сравнению с Arrays.copyOf обходится примерно на 10 % дешевле при маленьких массивах
Бенчмарк неправильный, соответственно, и выводы ложные.
Подсказка: в вызовеArrays.copyOf
надо заменитьsize
наoriginal.length
.apangin
27.07.2017 20:40+2Указал автору оригинальной статьи на ошибку, которую он, к чести, оперативно исправил. Выводы в статье поменялись. Неплохо бы теперь и перевод обновить, чтоб соответствовал новой редакции.
DigitalSmile
27.07.2017 17:28+3Хоть бы написали версию java, я уж не говорю о качестве бенчмарков и выводов на их основе…
izzholtik
Простите, но это нечитаемо. Оригинальная статья — та ещё каша, и бездумный и почти дословный перевод явно не пошёл ей на пользу.