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

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

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

1 ответ

Решение

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


PS В 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

Здесь стоит упомянуть, что другая популярная 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 может повысить производительность нативных методов до 3x, а @CriticalNative - до 5x.

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

Другие вопросы по тегам