Почему System.arraycopy является родным в Java?

Я был удивлен, увидев в источнике Java, что System.arraycopy - это родной метод.

Конечно, причина в том, что это быстрее. Но какие трюки - это код, который можно использовать, чтобы сделать его быстрее?

Почему бы просто не перебрать исходный массив и не скопировать каждый указатель на новый массив - неужели это не так медленно и громоздко?

Ответ 1

В собственном коде это можно сделать с помощью одного memcpy/memmove, в отличие от n различных операций копирования. Разница в производительности существенна.

Ответ 2

Он не может быть написан на Java. Собственный код способен игнорировать или исключать разницу между массивами объекта и массивами примитивов. Java не может этого сделать, по крайней мере, неэффективно.

И он не может быть записан с помощью одного memcpy() из-за семантики, требуемой перекрывающимися массивами.

Ответ 3

Это, конечно, зависит от реализации.

HotSpot будет относиться к нему как к "внутреннему" и вставить код на сайт вызова. Это машинный код, а не медленный старый код C. Это также означает, что проблемы с сигнатурой метода в значительной степени уходят.

Простой цикл копирования достаточно прост, чтобы к нему можно было применить очевидные оптимизации. Например, развертка цикла. То, что происходит, снова зависит от реализации.

Ответ 4

В моих собственных тестах 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

Ответ 5

Есть несколько причин:

  • JIT вряд ли будет генерировать эффективный код низкого уровня как написанный вручную C-код. Использование низкоуровневого C может позволить множество оптимизаций, которые практически невозможно сделать для общего JIT-компилятора.

    См. эту ссылку для некоторых трюков и скоростных сравнений написанных вручную C реализаций (memcpy, но принцип тот же): Проверьте это Оптимизация Memcpy улучшает скорость

  • Версия C почти не зависит от типа и размера элементов массива. Невозможно сделать то же самое в java, поскольку нет способа получить содержимое массива в виде необработанного блока памяти (например, указателя).