Что заставляет JNI звонить медленно?

Я знаю, что "пересечение границ" при вызове JNI в Java медленнее.

Однако я хочу знать, что это мешает? Что делает базовая реализация jvm при вызове JNI, что делает его настолько медленным?

Ответ 1

Во-первых, стоит отметить, что "медленно" мы говорим о чем-то, что может занять десятки наносекунд. Для тривиальных собственных методов в 2010 году я измерил вызовы в среднем 40 нс на рабочем столе Windows и 11 нс на моем рабочем столе Mac. Если вы не совершаете много звонков, вы не заметите.

Тем не менее, вызов собственного метода может быть медленнее, чем обычный вызов метода Java. Причины включают:

  • Собственные методы не будут включены в JVM. Они также не будут точно составлены для этой конкретной машины - они уже скомпилированы.
  • Ядро Java может быть скопировано для доступа в собственном коде и позже скопировано обратно. Стоимость может быть линейной по размеру массива. Я измерил JNI-копирование массива 100 000 в среднем на 75 микросекунд на моем рабочем столе Windows и 82 микросекундах на Mac. К счастью, прямой доступ можно получить через GetPrimitiveArrayCritical или NewDirectByteBuffer.
  • Если метод передан объекту или должен выполнить обратный вызов, тогда собственный метод, скорее всего, будет делать свои собственные вызовы для JVM. Доступ к Java-полям, методам и типам из собственного кода требует чего-то похожего на отражение. Подписи указываются в строках и запрашиваются из JVM. Это медленное и подверженное ошибкам.
  • Строки Java - это объекты, имеющие длину и закодированные. Для доступа или создания строки может потребоваться копия O (n).

Некоторые дополнительные обсуждения, возможно, датированные, можно найти в статье "Эффективность платформы Java: стратегии и тактика", 2000, Стив Уилсон и Джефф Кессельман в разделе "9.2: Изучение расходов JNI". Это примерно на треть пути вниз эта страница, приведенная в комментарии @Philip ниже.

В документе IBM developerWorks 2009 "Рекомендации по использованию Java-интерфейса" приводятся некоторые рекомендации по предотвращению ошибок производительности с JNI.

Ответ 2

Следует отметить, что не все Java-методы, отмеченные native, являются "медленными". Некоторые из них intrinsics, что делает их чрезвычайно быстрыми. Чтобы проверить, какие из них являются внутренними, а какие нет, вы можете искать do_intrinsic в vmSymbols.hpp.