Ответ 1
В собственном коде это можно сделать с помощью одного memcpy
/memmove
, в отличие от n различных операций копирования. Разница в производительности существенна.
Я был удивлен, увидев в источнике Java, что System.arraycopy - это родной метод.
Конечно, причина в том, что это быстрее. Но какие трюки - это код, который можно использовать, чтобы сделать его быстрее?
Почему бы просто не перебрать исходный массив и не скопировать каждый указатель на новый массив - неужели это не так медленно и громоздко?
В собственном коде это можно сделать с помощью одного memcpy
/memmove
, в отличие от n различных операций копирования. Разница в производительности существенна.
Он не может быть написан на Java. Собственный код способен игнорировать или исключать разницу между массивами объекта и массивами примитивов. Java не может этого сделать, по крайней мере, неэффективно.
И он не может быть записан с помощью одного memcpy()
из-за семантики, требуемой перекрывающимися массивами.
Это, конечно, зависит от реализации.
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
Есть несколько причин:
JIT вряд ли будет генерировать эффективный код низкого уровня как написанный вручную C-код. Использование низкоуровневого C может позволить множество оптимизаций, которые практически невозможно сделать для общего JIT-компилятора.
См. эту ссылку для некоторых трюков и скоростных сравнений написанных вручную C реализаций (memcpy, но принцип тот же): Проверьте это Оптимизация Memcpy улучшает скорость
Версия C почти не зависит от типа и размера элементов массива. Невозможно сделать то же самое в java, поскольку нет способа получить содержимое массива в виде необработанного блока памяти (например, указателя).