Я был удивлен, увидев в исходном коде Java, что System.arraycopy - это собственный метод.
Конечно, причина в том, что это быстрее. Но какие нативные уловки может использовать код, чтобы сделать его быстрее?
Почему бы просто не перебрать исходный массив и не скопировать каждый указатель в новый массив - конечно, это не так медленно и громоздко?
arraycopy
могут быть реализованы с использованиемmemcpy
/memmove
. Другие требуют проверки типа во время выполнения для каждого скопированного элемента.Object[]
заполненногоString
объектами вString[]
. См. Последний абзац java.sun.com/javase/6/docs/api/java/lang/…memcpy
иmemmove
являются O (n), однако из-за оптимизаций fe simd ониfew times
быстрее, поэтому вы можете сказать, что они O (n / x), где x зависит от оптимизаций, используемых в этих функцияхЕго нельзя написать на Java. Нативный код может игнорировать или исключать разницу между массивами Object и массивами примитивов. Java не может этого сделать, по крайней мере, неэффективно.
И это не может быть написано синглом
memcpy()
из-за семантики, необходимой для перекрывающихся массивов.источник
memmove
тогда. Хотя я не думаю, что это имеет большое значение в контексте этого вопроса.Это, конечно, зависит от реализации.
HotSpot будет рассматривать его как «внутренний» и вставлять код на сайте вызова. Это машинный код, а не медленный старый код C. Это также означает, что проблемы с сигнатурой метода в значительной степени исчезнут.
Простой цикл копирования достаточно прост, чтобы к нему можно было применить очевидные оптимизации. Например, разворачивание цикла. То, что происходит в точности, снова зависит от реализации.
источник
В моих собственных тестах System.arraycopy () для копирования множественных массивов измерений в 10-20 раз быстрее, чем чередование циклов:
float[][] foo = mLoadMillionsOfPoints(); // result is a float[1200000][9] float[][] fooCpy = new float[foo.length][foo[0].length]; long lTime = System.currentTimeMillis(); System.arraycopy(foo, 0, fooCpy, 0, foo.length); System.out.println("native duration: " + (System.currentTimeMillis() - lTime) + " ms"); lTime = System.currentTimeMillis(); for (int i = 0; i < foo.length; i++) { for (int j = 0; j < foo[0].length; j++) { fooCpy[i][j] = foo[i][j]; } } System.out.println("System.arraycopy() duration: " + (System.currentTimeMillis() - lTime) + " ms"); for (int i = 0; i < foo.length; i++) { for (int j = 0; j < foo[0].length; j++) { if (fooCpy[i][j] != foo[i][j]) { System.err.println("ERROR at " + i + ", " + j); } } }
Это печатает:
System.arraycopy() duration: 1 ms loop duration: 16 ms
источник
System.arraycopy
делает неглубокую копию (копируются только ссылки на внутренниеfloat[]
s), тогда как ваши вложенныеfor
-loops выполняют глубокую копию (float
byfloat
). ИзменениеfooCpy[i][j]
будет отражено вfoo
использованииSystem.arraycopy
, но не будет использовать вложенныеfor
циклы.Причин несколько:
JIT вряд ли создаст такой же эффективный код низкого уровня, как написанный вручную код C. Использование низкого уровня C может обеспечить множество оптимизаций, которые практически невозможно сделать для обычного JIT-компилятора.
См. Эту ссылку для некоторых приемов и сравнения скорости рукописных реализаций C (memcpy, но принцип тот же): Отметьте это Оптимизация Memcpy улучшает скорость
Версия C практически не зависит от типа и размера членов массива. Невозможно сделать то же самое в java, поскольку нет способа получить содержимое массива как необработанный блок памяти (например, указатель).
источник