Почему System.arraycopy является родным для Java? - PullRequest
77 голосов
/ 05 мая 2010

Я был удивлен, увидев в исходном коде Java, что System.arraycopy является нативным методом.

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

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

Ответы [ 5 ]

73 голосов
/ 05 мая 2010

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

15 голосов
/ 05 мая 2010

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

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

9 голосов
/ 05 мая 2010

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

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

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

4 голосов
/ 28 ноября 2011

В моих собственных тестах 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
3 голосов
/ 05 мая 2010

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

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

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

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

...