Разработка → Разбираемся с копированием и клонированием

перевод
AloneCoder 27 июля в 16:21 7,2k
Оригинал: Arnaud Roger


Я наткнулся на статью Нареша Джоши о копировании и клонировании и был удивлён ситуацией с производительностью. У клонирования есть проблемы с финальными полями. А учитывая тот факт, что интерфейс Cloneable не предоставляет метод clone, то для вызова clone вам необходимо будет знать конкретный тип класса.


Вы можете написать такой код:


    ((Cloneable)o).clone(); // не работает

Если интерфейс Cloneable сломан, то у механизма клонирования могут быть некоторые преимущества. При копировании памяти он может оказаться эффективнее, чем копирование поля за полем. Это подчёркивает Джош Блох, автор Effective Java:


Даг Ли пошёл ещё дальше. Он сказал мне, что теперь клонирует только при копировании массивов. Вам следует использовать клонирование копирования массивов, потому что в целом это самый быстрый способ. Но у Дага типы больше не реализуют Cloneable. Он с ним завязал. И я не считаю это необоснованным.

Но это было в 2002-м, разве ситуация не изменилась? Со времён Java 6 у нас есть Arrays.copyOf, что насчёт него? Какова производительность копирования объекта?
Есть только один способ выяснить: прогнать бенчмарки.


TL;DR


  • Клонирование работает быстрее при копировании массива, это заметно на маленьких массивах.
  • Arrays.copyOf и clone примерно одинаково работают
  • Клонирование работает медленнее для маленьких объектов, меньше восьми полей, но в любом случае быстрее.
  • При клонировании не работает escape analysis, и потенциально оно может помешать применению других оптимизаций.

image


Массивы


[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 полями:


Проголосовать:
+14
Сохранить: