Що робить дзвінки JNI повільними?


194

Я знаю, що "перетинання кордонів" під час здійснення дзвінка JNI на Яві відбувається повільно.

Однак я хочу знати, що це робить це повільним? Що робить основна реалізація jvm при здійсненні виклику JNI, що робить його настільки повільним?


2
(+1) Приємне запитання. Хоча ми з цього питання, я хотів би заохотити всіх, хто зробив фактичні орієнтири, розмістити свої висновки.
NPE

2
Дзвінок JNI повинен перетворити передані об'єкти Java в те, що C (наприклад) може зрозуміти; те ж саме із значенням повернення. Перетворення типів і стек виклику - це хороший фрагмент.
Дейв Ньютон

Дейв, я розумію і чув про це раніше. Але що саме таке перетворення? що це "щось"? Шукаю деталі.
pdeva

Використання прямих ByteBuffers для передачі даних між Java та C може призвести до порівняно низьких накладних витрат.
Пітер Лорей

6
для виклику потрібен належний кадр стека C, відштовхуючи всі корисні регістри процесора (і вискакуючи їх назад), виклик потребує фехтування, а також запобігає багато оптимізацій, таких як вбудований. Також потоки повинні залишити блокування стека виконання (наприклад, щоб дозволити блокувати роботу блоків під час використання коду) та повернути його назад.
bestsss

Відповіді:


174

По-перше, варто зазначити, що "повільно" ми говоримо про те, що може зайняти десятки наносекунд. Для тривіальних нативних методів я в 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 Native» наведено кілька пропозицій щодо уникнення підводних процесів з використанням JNI.


1
Ця відповідь стверджує, що JVM може накреслити якийсь нативний код .
AH

5
У цій відповіді зазначається, що деякий стандартний нативний код вводиться в JVM, а не використовувати JNI. Вище "природні методи" стосуються загального випадку призначених користувачем нативних методів, реалізованих за допомогою JNI. Дякуємо за вказівник на sun.misc.Unsafe.
Енді Томас

Я не хотів стверджувати, що цей підхід можна використовувати для кожного дзвінка в JNI. Але не завадить дізнатись, що між чистим байт-кодом та чистим кодом JNI є середнє місце. Можливо, це вплине на деякі дизайнерські рішення. Можливо, цей механізм узагальнений у майбутньому.
AH

3
@AH, ти помиляєш внутрішню w / JNI. Вони зовсім інші. sun.misc.Unsafeі дуже багато інших подібних речей System.currentTimeMillis/nanoTime, якими керує JVM за допомогою "магії". Вони не JNI, і вони взагалі не мають належних файлів .c / .h, що забороняє імпульс JVM. Підходу не можна дотримуватися, якщо ви не пишете / не рубаєте JVM.
bestsss

1
" цей документ java.sun.com " наразі зламаний - ось робоче посилання.
Філіп Гуін

25

Варто зазначити, що не всі методи Java, позначені символом, nativeє "повільними". Деякі з них є властивими, що робить їх надзвичайно швидкими. Щоб перевірити, які з них є внутрішніми, а які - не, ви можете подивитися do_intrinsicна vmSymbols.hpp .


23

В основному JVM інтерпретативно будує параметри С для кожного дзвінка JNI, і код не оптимізований.

У цій роботі викладено ще багато деталей

Якщо вас цікавить тестування JNI проти нативного коду, цей проект має код для запуску тестів.


2
папір, з якою ви пов'язуєтесь, здається більше схожим на документ про ефективність, ніж на той, що описує, як працює JNI.
pdeva

@pdeva На жаль, інші знайдені нами ресурси були пов’язані з java.sun.com, і посилання не були оновлені з моменту придбання Oracle. Я шукаю більш детальну інформацію про внутрішні сили JNI.
dmck

13
Документ про Java 1.3 - досить давно. Чи все ще актуальні питання того часу стосуються Java 7?
AH
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.