Почему это система.arraycopy родной в Java?


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

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

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

5 76

5 ответов:

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

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

и не могу пишется с одним memcpy() из-за семантики требует дублирования массивов.

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

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

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

в моей собственной тестовой системе.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

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

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

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

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