Можно ли использовать sun.misc.Unsafe для вызова функций C без JNI?

Фрагмент кода C/C++ может предоставить методу JNI массив указателей на функции. Но есть ли способ вызвать в стеке функции, на которые указывают указатели массива, непосредственно из кода Java (без использования JNI или подобного)? JNI каким-то образом делает что-то подобное, поэтому должен быть какой-то способ. Как JNI это делает? Это через sun.misc.Unsafe? Даже если это не так, можем ли мы использовать небезопасный обходной путь, чтобы получить в свои руки код JVM, который делает это?

Я не планирую использовать это в коммерческих целях, конечно. Я даже не профессионал, мне просто очень нравится программировать, и в последнее время я изучаю CUDA, поэтому я подумал, что, может быть, я мог бы поэкспериментировать со смешиванием всего вместе, но накладные расходы на вызовы JNI свели бы на нет цель иметь ускоренный код на GPU.


person FinnTheHuman    schedule 30.03.2016    source источник
comment
JNI делает это, будучи встроенным в JVM.   -  person user253751    schedule 30.03.2016


Ответы (2)


JNI такой медленный?

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

Эти накладные расходы могут быть значительными, если нативная функция проста и вызывается часто. JDK имеет частный API под названием Critical Natives, чтобы уменьшить накладные расходы на вызов функций, которые не требуют значительной части функций JNI.

Критически настроенные туземцы

Нативный метод должен удовлетворять следующим условиям, чтобы стать критическим нативным:

  • должен быть статичным и не синхронизированным;
  • типы аргументов должны быть примитивными или примитивными массивами;
  • реализация не должна вызывать функции JNI, т. е. она не может выделять объекты Java или генерировать исключения;
  • не должен работать в течение длительного времени, так как он заблокирует GC во время работы.

Объявление критического натива выглядит как обычный метод JNI, за исключением того, что

  • он начинается с JavaCritical_ вместо Java_;
  • у него нет лишних аргументов JNIEnv* и jclass;
  • Массивы Java передаются в двух аргументах: первый — это длина массива, а второй — указатель на необработанные данные массива. То есть не надо вызывать GetArrayElements и друзей, можно сразу использовать прямой указатель массива.

Например. метод JNI

JNIEXPORT jint JNICALL
Java_com_package_MyClass_nativeMethod(JNIEnv* env, jclass klass, jbyteArray array) {
    jboolean isCopy;
    jint length = (*env)->GetArrayLength(env, array);
    jbyte* buf = (*env)->GetByteArrayElements(env, array, &isCopy);
    jint result = process(buf, length);
    (*env)->ReleaseByteArrayElements(env, array, buf, JNI_ABORT);
    return result;    
}

обратится к

JNIEXPORT jint JNICALL
JavaCritical_com_package_MyClass_nativeMethod(jint length, jbyte* buf) {
    return process(buf, length);
}

Критические нативы поддерживаются только в HotSpot JVM, начиная с JDK 7. Более того, «критическая» версия вызывается только из скомпилированного кода. Поэтому вам нужна как критическая, так и стандартная реализация, чтобы это работало правильно.

Эта функция была разработана для внутреннего использования в JDK. Там нет публичной спецификации или что-то в этом роде. Вероятно, единственная документация, которую вы можете найти, находится в комментариях к JDK-7013347.

Ориентир

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

Производительность вызовов JNI


P.S. В JDK продолжается работа по реализации Native MethodHandles, которые будут служить более быстрой альтернативой JNI. Однако вряд ли он появится до JDK 10.

  1. http://cr.openjdk.java.net/~jrose/panama/native-call-primitive.html
  2. http://mail.openjdk.java.net/pipermail/panama-dev/2015-December/000225.html
person apangin    schedule 30.03.2016
comment
Как узнать, будет ли ваша программа использовать критическую реализацию или стандартную? Я только что попробовал аналогичный пример, где я скомпилировал свой java-файл с javac и запустил его, и он использовал стандартную некритическую версию. - person Ivaylo Toskov; 05.05.2017
comment
@IvayloToskov Только методы, скомпилированные JIT, вызывают Critical Natives. Пока метод интерпретируется, он вызывает стандартную реализацию. - person apangin; 05.05.2017
comment
Должно ли объявление метода присутствовать как в файлах .c, так и в файлах .h? - person Jaspreet; 27.12.2018
comment
@Jaspreet Нет необходимости иметь это в .h - person apangin; 27.12.2018
comment
Будет ли jbyteArray всегда разбиваться на указатель и значение длины или при желании он останется рабочим объектом? Я предпочитаю брать объект задания, а затем ссылаться на указатели, чтобы определить, является ли он массивом или длинным значением. Есть ли какой-либо источник в JVM, который вы можете указать, чтобы лучше объяснить это поведение? - person Johnny V; 23.04.2019
comment
@JohnnyV Нет, вы не можете передать задание критически настроенному пользователю. В любом случае это будет бесполезно, так как никакие функции JNI не разрешены внутри критических нативов. - person apangin; 23.04.2019
comment
@apangin Сегодня я провел несколько экспериментов и обнаружил, что JavaCritical практически не имеет значения, когда функции JNI полностью не используются. Стоимость JNI очень похожа и все еще во много раз дороже, чем внутренний вызов Java. Я как бы надеялся на серебряную пулю для создания вызовов функций без затрат на JNI. - person Johnny V; 23.04.2019
comment
Быстрый тест, чтобы проверить, включены ли CriticalNatives (флаг -XX:+CriticalJNINatives) java -XX:+PrintFlagsFinal -version 2>&1 | grep CriticalJNINatives. Также стоит отметить, что JVM можно заставить компилировать все методы при их первом вызове, используя флаг -Xcomp. Ссылка: github.com/openjdk/shenandoah/ дерево/мастер/тест/горячая точка/jtreg/ - person krakowski; 05.02.2020
comment
Появился ли Native MethodHandles в JDK 10? - person turbanoff; 22.10.2020
comment
@turbanoff Нет даже в JDK 15. Но есть некоторый прогресс хотя. - person apangin; 22.10.2020

Здесь стоит упомянуть, что другая популярная JVM с открытым исходным кодом имеет аналогичный, задокументированный, но не популярный способ ускорения вызовов JNI для некоторых собственных методов.

Более быстрые собственные вызовы Java Native Interface (JNI) доступны с помощью аннотаций @FastNative и @CriticalNative. Эти встроенные средства оптимизации времени выполнения ART ускоряют переходы JNI и заменяют устаревшую нотацию !bang JNI. Аннотации не влияют на несобственные методы и доступны только для кода языка Java платформы в пути загрузочного класса (без обновлений Play Store).

Аннотация @FastNative поддерживает нестатические методы. Используйте это, если метод обращается к объекту задания в качестве параметра или возвращаемого значения.

Аннотация @CriticalNative обеспечивает еще более быстрый способ запуска собственных методов со следующими ограничениями:

  • Методы должны быть статическими — никаких объектов для параметров, возвращаемых значений или неявного this.
  • В собственный метод передаются только примитивные типы.
  • Собственный метод не использует параметры JNIEnv и jclass в определении своей функции.
  • Метод должен быть зарегистрирован в RegisterNatives вместо того, чтобы полагаться на динамическую компоновку JNI.

Аннотации @FastNative и @CriticalNative отключают сборку мусора при выполнении нативного метода. Не используйте с долго выполняющимися методами, в том числе обычно быстрыми, но обычно неограниченными методами.

Приостановка сборки мусора может привести к взаимоблокировке. Не запрашивайте блокировки во время быстрого собственного вызова, если блокировки не были сняты локально (т. е. перед возвратом к управляемому коду). Это не относится к обычным вызовам JNI, поскольку ART считает выполнение машинного кода приостановленным.

@FastNative может повысить производительность собственных методов до 3 раз, а @CriticalNative — до 5 раз.

Этот документ относится к устаревшей нотации !bang, которая использовалась для ускорения некоторых собственных вызовов в Dalvik JVM.

person Alex Cohn    schedule 23.02.2019