Подтвердить что ты не робот

Накладные расходы на вызов JNI Java

Возможный дубликат:
Что заставляет JNI звонить медленнее?

Сначала позвольте мне сказать, что эти вопросы рождаются больше из любопытства, чем настоящая необходимость.

Мне любопытно узнать, что накладные расходы на выполнение JNI-вызова из Java, скажем, System.arraycopy по сравнению с распределением массива и копированием элементов с циклом for.

Если накладные расходы существенны, то, вероятно, есть приблизительное "магическое число" элементов, до которых он компенсирует просто использование цикла for, вместо использования системного вызова. А также, что именно участвует в системном вызове, который вызывает эти накладные расходы? Я предполагаю, что стек должен быть перенесен в контекст вызова, и это может занять некоторое время, но я не могу найти хорошее объяснение для всего процесса.

Позвольте мне уточнить мой вопрос:

Я знаю, что использование arraycopy - это самый быстрый способ скопировать массив в Java.

Чтобы сказать, что я использую его, чтобы скопировать массив только одного элемента. Поскольку я вызываю базовую ОС для этого, в этом вызове должны быть накладные расходы. Мне интересно узнать, что такое накладные расходы и что происходит в процессе вызова.

Прошу прощения, если использование arraycopy вводит вас в заблуждение с целью моего вопроса. Мне интересно узнать накладные расходы на вызов JNI и то, что связано с фактическим вызовом.

4b9b3361

Ответ 1

Поскольку я вызываю базовую ОС для этого...

Вы правы, что системные вызовы довольно дороги. Однако System in System.arraycopy() является немного неправильным. Системных вызовов нет.

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

Когда вы смотрите определение System.arraycopy(), оно объявляется как native. Это означает, что метод реализован на С++. Если вы так наклонены, вы можете посмотреть исходный код JDK и найти функцию С++. В OpenJDK 7 он называется JVM_ArrayCopy() и живет в hotspot/src/share/vm/prims/jvm.cpp. Реализация на удивление сложна, но в глубине она существенно memcpy().

Если arraycopy() используется как обычная нативная функция, для вызова его накладных расходов. Дальнейшие издержки вызваны проверкой аргументов и т.д.

Однако очень вероятно, что JIT-компилятор знает о System.arraycopy(). Это означает, что вместо вызова функции С++ компилятор знает, как создать специально созданный машинный код для выполнения копии массива. Я не знаю о других JVM, но HotSpot имеет такую ​​ "внутреннюю" поддержку для System.arraycopy().

Скажем, я использую его для копирования массива только одного элемента

Если ваш массив мал, вы можете бить System.arraycopy() с помощью обработанного вручную цикла. Вероятно, вы можете сделать еще лучше, если размер известен во время компиляции, так как вы также можете развернуть цикл. Однако все это не актуально, за исключением самых суровых обстоятельств.

Ответ 2

Взгляните на реализацию java.util.Arrays.copyOf, например

public static byte[] copyOf(byte[] original, int newLength) {
    byte[] copy = new byte[newLength];
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

они используют System.arraycopy, потому что это самый быстрый способ.

Если вы имеете в виду, стоит ли вызывать собственные методы на Java, то посмотрите http://www.javamex.com/tutorials/jni/overhead.shtml

ОБНОВЛЕНИЕ Вопрос действительно интересный, поэтому я провел некоторое тестирование

        long t0 = System.currentTimeMillis();
        byte[] a = new byte[100];
        byte[] b = new byte[100];
        for(int i = 0; i < 10000000; i++) {
//            for(int j = 0; j < a.length; j++) {
//                a[j] = b[j];
//            }
            System.arraycopy(b, 0, a, 0, a.length);
        }
        System.out.println(System.currentTimeMillis() - t0);

Это показывает, что на очень коротких массивах (< 10) System.arraycopy может быть еще медленнее, скорее всего, потому что он родной, но на больших массивах это уже не имеет значения, System.arraycopy намного быстрее.

Ответ 3

Мне интересно узнать накладные расходы на вызов JNI и то, что связано с фактическим вызовом.

Метод System.arraycopy() довольно сложный * и маловероятно, чтобы компилятор JIT ввел его (как показывает один из других ответов).

С другой стороны, вероятно, компилятор JIT использует оптимизированную последовательность вызовов, так как это внутренний собственный метод. Другими словами, это скорее всего не обычный вызов JNI.


* - System.arraycopy не является простой копией памяти. Он должен проверять свои аргументы, чтобы избежать чтения или записи за пределами массива и т.д. И в случае, когда вы копируете из одного массива объектов в другой, может потребоваться проверить, что фактический тип каждого объекта скопирован. Все это добавляет гораздо больше кода, чем разумно встроить.

Ответ 4

У вас это неправильно. System.arraycopy() - сверхбыстрая нативная реализация, предоставляемая JVM

Нет "накладных расходов" - есть только "преимущество"