Comments 53
Интересно узнать про методику выноса части функционала в C++ модуль.
Например, мне нужно выполнить ресурсоемкую операцию с изображением, и я не хочу это делать средствами Java.
Допустим, я подаю C-методу на вход картинку в виде массива байтов, Сишный код выполняет операцию с изображением и возвращет мне обратно массив байтов, дальше, средствами Джавы, я отображаю ее пользователю.
Возможно ли такое?
Стоит ли шкура выделки? Могу ли я таким образом повысить производительность своего приложения?
Спасибо!
Например, мне нужно выполнить ресурсоемкую операцию с изображением, и я не хочу это делать средствами Java.
Допустим, я подаю C-методу на вход картинку в виде массива байтов, Сишный код выполняет операцию с изображением и возвращет мне обратно массив байтов, дальше, средствами Джавы, я отображаю ее пользователю.
Возможно ли такое?
Стоит ли шкура выделки? Могу ли я таким образом повысить производительность своего приложения?
Спасибо!
Конечно возможно. Как передавать данные со структурой сложнее, чем у примитивных типов, я опишу в следующей части.
На счет вопроса, можно ли таким образом получить ускорение. Далеко не обязательно. В java есть JIT, который неплохо делает свою работу. С другой стороны, управляя памятью вручную, мы снимаем нагрузку с GC, что может дать некоторое ускорение. Вообщем, нужно хорошо понимать, что вы делаете :)
На счет вопроса, можно ли таким образом получить ускорение. Далеко не обязательно. В java есть JIT, который неплохо делает свою работу. С другой стороны, управляя памятью вручную, мы снимаем нагрузку с GC, что может дать некоторое ускорение. Вообщем, нужно хорошо понимать, что вы делаете :)
Мне кажется, не стоит передавать изображение в виде массива байтов. Лучше записать его во временный файл и передать С++ лишь имя файла (да и вообще в джаве весь файл целиком не стоит считывать, имхо). По скорости это не будет особо влиять на нормальных фс (типа xfs, где если у вас много оперативки — файл останется в RAM некоторое время).
Плюсы — не забивается память (а точнее умно управляется фс), скорость максимальная.
Плюсы — не забивается память (а точнее умно управляется фс), скорость максимальная.
Решение хорошее для небольших нагрузок, но что если надо обрабатывать видео? 30+ кадров в секунду к примеру. И так в несколько потоков. Бедная ФС не справится :)
По идее должна справляться, но в любом случае всегда можно жестко указать что файл будет храниться в RAM'е. Такой-себе юникс-вей (не мешают ведь возможности фс делать виртуальную фс для устройств).
Насколько я понимаю, с потоковым видео вообще это вещь хорошая (чтоб поток шёл в файл, а программка уже этот файл обрабатывала), ведь ОС справится куда лучше с нормальным получением потока в файл при хороших нагрузках чем ваша программа с двумя задачами сразу (и получение и обработка). И если программа чуть (на секунду) зависнет, ос, скорее всего, с потоком справится нормально.
p.s.: это так, «мысли вслух», подобным не занимался, потому интересно от «практиков» услышать как оно на самом деле надо делать)
Насколько я понимаю, с потоковым видео вообще это вещь хорошая (чтоб поток шёл в файл, а программка уже этот файл обрабатывала), ведь ОС справится куда лучше с нормальным получением потока в файл при хороших нагрузках чем ваша программа с двумя задачами сразу (и получение и обработка). И если программа чуть (на секунду) зависнет, ос, скорее всего, с потоком справится нормально.
p.s.: это так, «мысли вслух», подобным не занимался, потому интересно от «практиков» услышать как оно на самом деле надо делать)
Да вы что, с чем ОС справится лучше?! Лучше чего?
Файловая система — особенно в многозадачной среде — опасный союзник! У вас нет никаких прогнозов на скорость доступа к ней, какой тут реалтайм. Сейчас ваш файл буферезирован, а через секунду кто-то запросил памяти и буфер слит. И ваш код ждет данных неивестно сколько — как совпадут звезды, фрагментированность фс, загруженность очереди доступа к ней и еще бог знает сколько факторов.
Если файловая система живет в ядре — с большой вероятностью практически каждая операция доступа к ней — переключение в режим ядра, что весьма небыстро (вы все еще хотите реалтайма? =) ). От этого спасает опять-таки буферизация, но вы ведь память экономить хотели?
В общем говоря, если вам нужна скорость и детерменнированность — данные нужно передавать только в памяти. Нужна экономия — ищите способ минимизировать эти данные — передавать их блоками, слайсами, фреймами, как угодно.
Файловая система — особенно в многозадачной среде — опасный союзник! У вас нет никаких прогнозов на скорость доступа к ней, какой тут реалтайм. Сейчас ваш файл буферезирован, а через секунду кто-то запросил памяти и буфер слит. И ваш код ждет данных неивестно сколько — как совпадут звезды, фрагментированность фс, загруженность очереди доступа к ней и еще бог знает сколько факторов.
Если файловая система живет в ядре — с большой вероятностью практически каждая операция доступа к ней — переключение в режим ядра, что весьма небыстро (вы все еще хотите реалтайма? =) ). От этого спасает опять-таки буферизация, но вы ведь память экономить хотели?
В общем говоря, если вам нужна скорость и детерменнированность — данные нужно передавать только в памяти. Нужна экономия — ищите способ минимизировать эти данные — передавать их блоками, слайсами, фреймами, как угодно.
Не вижу разницы между хранением потока в оперативки и memory mapped file ни по скорости ни по сливу на жесткий диск (или я ошибаюсь?)
А почему хочется такого (с файлом)? Очень просто: к примеру, можно задать входной поток подпрограмме как файл (типа внешний API для тестирования в том числе уже готов).
Или все-таки эти файлы в оперативке которые куда медленнее кидания буферами между модулями?
А почему хочется такого (с файлом)? Очень просто: к примеру, можно задать входной поток подпрограмме как файл (типа внешний API для тестирования в том числе уже готов).
Или все-таки эти файлы в оперативке которые куда медленнее кидания буферами между модулями?
Смотрите, если вам нужно передать данные, реально много данных, которые будут долго обрабатываться и вам совершенно все равно с какой производительностью — MMF отличный выбор. Более того, это похоже вообще лучший выбор при пересечении границ процессов. Внутри же одного процесса…
Если вам важна производительность — быстрее блока памяти вы ничего не придумаете.
Если вам важна экономия памяти — нужно перерабатывать алгоритмы на работу с данными небольшими порциями. mp3 жмет входные данные по 1152 семпла, jpeg жмет картинку блоками 8х8 пикселей.
Если вам нужен удобный интерфейс — заведите его! Хотите стандартный — посмотрите на комовский IStream, например. Если вам нужны только потоковые функции, зачем притягивать использование ядра ОС и файловой системы?
Если вам важна производительность — быстрее блока памяти вы ничего не придумаете.
Если вам важна экономия памяти — нужно перерабатывать алгоритмы на работу с данными небольшими порциями. mp3 жмет входные данные по 1152 семпла, jpeg жмет картинку блоками 8х8 пикселей.
Если вам нужен удобный интерфейс — заведите его! Хотите стандартный — посмотрите на комовский IStream, например. Если вам нужны только потоковые функции, зачем притягивать использование ядра ОС и файловой системы?
> Если вам важна производительность — быстрее блока памяти вы ничего не придумаете.
Более чем согласен. У меня просто в другом чуть вопрос. На сколько я понял, оно отлично преобразует типы и классы из джавовских в C++'ные. Скорее всего это делается клонированием? Или я не прав?
Тоесть когда вы передаёте этот самый блок памяти, C++ имеет доступ именно к нему (без излишеств?) Если да — всё ясно, вы правы насчет производительности :-)
Более чем согласен. У меня просто в другом чуть вопрос. На сколько я понял, оно отлично преобразует типы и классы из джавовских в C++'ные. Скорее всего это делается клонированием? Или я не прав?
Тоесть когда вы передаёте этот самый блок памяти, C++ имеет доступ именно к нему (без излишеств?) Если да — всё ясно, вы правы насчет производительности :-)
Тогда зачем вообще JNI? Если работать через файл, то можно написать консольную программку обработки на сях и вызывать ее через Runtime.exec().
JNI нужен именно там, где необходима более близкая интеграция java, например для проксирования вызовов к операционке.
JNI нужен именно там, где необходима более близкая интеграция java, например для проксирования вызовов к операционке.
DirectBuffer'а оказываются весьма полезными для передачи больших массивов байтов между нативным и Java кодом…
Возможно, при помощи библиотеки описанной мной в комментариях ниже, я как раз реализовывал приём картинок из dll в java приложение путем возврата указателя на массив байтов…
По моему опыту будет быстрее сделать все в Java, хотя зависит от операций, которые вы хотите проводить. На элементарных операциях Java не будет проигрывать С++ — при первом вызове функции JIT скомпилит ее в native, поэтому медленным будет только этот первый вызов.
Нужно также помнить, что JNI сравнительно медленный.
Нужно также помнить, что JNI сравнительно медленный.
Такое возможно, но, скорее всего, не стоит.
Я это все плохо понимаю, если проясните, буду благодарен.
А как быть, если мне напрамер нужно в С++ модуль передать переменную не простого типа, а какой-нибудь класс? Или наоборот, у меня есть класс написанный на С++ и один из методов должен вернуть объект данного класса, как здесь быть? То есть как производить обмен между модулями используя более сложные структуры чем примитивные типы и String?
А так очень интересно, естественно, продолжайте. По этой тематике маловато обучающих статей.
JNI формально поддерживает только вызовы в стиле Си. Но так как C++ является расширением языка C, то возможно на основе паттерна проектирования создать прокси-объекты для преобразования структуры объекта Java-языка в серию нативных вызовов через C-функции к объекту, написанному на C++. Или же упаковать данные объекта в пакет данных (массив или структуру) и через JNI передать его в другую среду выполнения, сделав всего один вызов вместо нескольких.
Для доступа к объекту из С++ есть специальные методы методы GetMethodID, GetFieldID ну и другие. Это неплохо описано в java.sun.com/j2se/1.5.0/docs/guide/jni/spec/functions.html
Перенесите в блог Java, пожалуйста.
Лет семь назад у меня получилось подружить Java и Delphi (.dll) — использовал специальный модуль конвертации типов C<->Pascal, а заголовочные файлы, полученные javah, переводил на Delphi Pascal вручную (это не сложно).
Есть не плохая книга Взаимодействие разноязыковых программ в Microsoft Windows. Руководство программиста. Там это довольно подробно описано, также еть и описание того как связать C++ и Perl, да и еще много чего.
Продолжайте! У вас получилось рассказать о нетривиальной задаче довольно просто. Как-то читал про JNI и мало что понял, а теперь всё встало на свои места. Спасибо.
Мне тоже приходилось как-то соединять web-приложение с DLL написанной на Deplhi,
причем одним из условий было то, что перекомпилить DLL для JNI я не мог.
Успешно вышел из ситуации при помощи JNA (https://jna.dev.java.net/):
написал синглтон класс DllWrapper в котором создавался инстанс dll объекта
DllLibInterface dllInst = (DllLibInterface) Native.loadLibrary(«test.dll», DllLibInterface.class);
далее у dllInst можно было вызывать его вложенные функции…
В свою очередь что бы избежать паралельного обращения к одним и тем же функциям DLL
из разных сессий веб приложения — для каждой dll функции в классе DllWrapper были созданы
отдельные synchronized функции обертки…
С синтаксисом передаваемых и возвращаемых объектов разобраться по вышеуказанному сайту
довольно легко.
Надеюсь мой комментарий кому-нибудь облегчит жизнь. ;-)
причем одним из условий было то, что перекомпилить DLL для JNI я не мог.
Успешно вышел из ситуации при помощи JNA (https://jna.dev.java.net/):
написал синглтон класс DllWrapper в котором создавался инстанс dll объекта
DllLibInterface dllInst = (DllLibInterface) Native.loadLibrary(«test.dll», DllLibInterface.class);
далее у dllInst можно было вызывать его вложенные функции…
В свою очередь что бы избежать паралельного обращения к одним и тем же функциям DLL
из разных сессий веб приложения — для каждой dll функции в классе DllWrapper были созданы
отдельные synchronized функции обертки…
С синтаксисом передаваемых и возвращаемых объектов разобраться по вышеуказанному сайту
довольно легко.
Надеюсь мой комментарий кому-нибудь облегчит жизнь. ;-)
Преимущество JNA перед JNI — что на надо определенным образом компилировать DLL библиотеку.
Это очень полезное свойство при использовании функционала который вы не можете никак изменить как например windows API функции.
Из найденых касяков — если DLL написана в Borland C++ Builder, то она не будет «коннектится» с java
до тех пора пока в ней не будет убрано всякое упоминание о VCL.
Это очень полезное свойство при использовании функционала который вы не можете никак изменить как например windows API функции.
Из найденых касяков — если DLL написана в Borland C++ Builder, то она не будет «коннектится» с java
до тех пора пока в ней не будет убрано всякое упоминание о VCL.
Спасибо за статью!
Жду продолжения!
Особенно интересуют особенности использование native-кода при наследовании классов и обработка исключений.
Жду продолжения!
Особенно интересуют особенности использование native-кода при наследовании классов и обработка исключений.
Интересная серия статей про JNI, особо рассматривается передача строк, констант и структур между Java code и native сode
community.livejournal.com/levin_matveev/29429.html
community.livejournal.com/levin_matveev/29449.html
community.livejournal.com/levin_matveev/29750.html
community.livejournal.com/levin_matveev/30803.html
community.livejournal.com/levin_matveev/29429.html
community.livejournal.com/levin_matveev/29449.html
community.livejournal.com/levin_matveev/29750.html
community.livejournal.com/levin_matveev/30803.html
При использовании C++ для написания JNI методов нужно иметь в виду, что могут возникать C++ exceptions, которые не мог быть обработаны JVM, так как используется C ABI. Поэтому нужно корректно перехватывать эти исключения и либо конвертировать их в Java исключения, либо корректно завершать работу JVM. При использовании чистого C такой проблемы нет.
Другой важный момент — это то, что Java exception, брошенный в нативном методе, не прерывает работу нативного метода в точке выброса exception.
Когда имеет смысл использовать JNI:
— имеется только нативная версия библиотеки с каторой нужно работать
— нужен доступ к ресурсам, которым JVM не предоставляет доступ или доступ реализован очень плохо
На текущий момент JVM JIT генерирует очень хороший код и задача написать быстрый JNI код — это нетривиальная задача, прикручивание в лоб обычно никакого прироста может не дать. У меня есть живые примеры, когда к нативной библиотеке, которая в 5 раз быстрее соотвествующей чистой Java библиотеки, прикручивали JNI интерфейс, итоговая производительность была всего лишь чуть-чуть больше чистой Java. Накладные расходы по JNI вызовам очень большие, поэтому желательно уходить в натив надолго, слишком частые переключения контекстов испольнения значительно снизять производительность. Еще один факт из жизни, чем сложнее данные которые нужно передовать между Java и нативом, тем медленее все работает. Все работает достаточно быстро при использовании примитивных типов, массивов примитовного типа и String.
Очень-очень большие минусы JNI кода:
— JNI код — это огромная security дыра в Java security-модели, JVM не имеет никаких средств по контролю JNI кода и JNI код может делать что угодно, в том числе получать доступ к приватным данным.
— Утечки памяти. Память которая выделяется в JNI коде, за исключение Java объектов созданных в нативе, выделяется в общем heap приложения, а не в Java-heap. В Java нет деструкторов, поэтому может возникнут ситуация, когда что-то навыделялось в нативе и уже не нужно, но оно не освобождается. Пример:
Пусть у нас есть Java объект, состоящий из Java данных и C++ данных (создаем в нативе С++ объект и храним handle на него в Java объекте), удаление C++ объекта помещаем в метод finalize, так как вызов finalize метода не дерминирован, то у нас возникают утечки памяти. С точки зрения GC проводить сборку мусора не нужно, так как Java-heap имеет достаточно места.
Еще одна головная боль — это отладка JNI кода, приходится использовать два дебагера: Java кода и нативного кода. Вроде для Eclipse есть какой-то дебагер который позволяет отлаживать как Java код, так и JNI код.
Другой важный момент — это то, что Java exception, брошенный в нативном методе, не прерывает работу нативного метода в точке выброса exception.
Когда имеет смысл использовать JNI:
— имеется только нативная версия библиотеки с каторой нужно работать
— нужен доступ к ресурсам, которым JVM не предоставляет доступ или доступ реализован очень плохо
На текущий момент JVM JIT генерирует очень хороший код и задача написать быстрый JNI код — это нетривиальная задача, прикручивание в лоб обычно никакого прироста может не дать. У меня есть живые примеры, когда к нативной библиотеке, которая в 5 раз быстрее соотвествующей чистой Java библиотеки, прикручивали JNI интерфейс, итоговая производительность была всего лишь чуть-чуть больше чистой Java. Накладные расходы по JNI вызовам очень большие, поэтому желательно уходить в натив надолго, слишком частые переключения контекстов испольнения значительно снизять производительность. Еще один факт из жизни, чем сложнее данные которые нужно передовать между Java и нативом, тем медленее все работает. Все работает достаточно быстро при использовании примитивных типов, массивов примитовного типа и String.
Очень-очень большие минусы JNI кода:
— JNI код — это огромная security дыра в Java security-модели, JVM не имеет никаких средств по контролю JNI кода и JNI код может делать что угодно, в том числе получать доступ к приватным данным.
— Утечки памяти. Память которая выделяется в JNI коде, за исключение Java объектов созданных в нативе, выделяется в общем heap приложения, а не в Java-heap. В Java нет деструкторов, поэтому может возникнут ситуация, когда что-то навыделялось в нативе и уже не нужно, но оно не освобождается. Пример:
Пусть у нас есть Java объект, состоящий из Java данных и C++ данных (создаем в нативе С++ объект и храним handle на него в Java объекте), удаление C++ объекта помещаем в метод finalize, так как вызов finalize метода не дерминирован, то у нас возникают утечки памяти. С точки зрения GC проводить сборку мусора не нужно, так как Java-heap имеет достаточно места.
Еще одна головная боль — это отладка JNI кода, приходится использовать два дебагера: Java кода и нативного кода. Вроде для Eclipse есть какой-то дебагер который позволяет отлаживать как Java код, так и JNI код.
Согласен со всем. Просто нужно понимать, что делаешь и когда это нужно применять.
Кстати, добавлю. Оверхед может появиться даже при передаче массивов примитивного типа и строк, потому что JVM в некоторых случаях может решить, что их нужно скопировать.
Кстати, добавлю. Оверхед может появиться даже при передаче массивов примитивного типа и строк, потому что JVM в некоторых случаях может решить, что их нужно скопировать.
Ну самый большой проблем всеж технологии, именно в «накладные расходы по JNI вызовам очень большие», все остальное не настолько существенно.
Я думаю не стоит забывать что ява программы и билиотеки можно откомпилировать омпилятором gcj и работать с ними через CNI(Cygnus Native Interfase)
Вопрос чуток не по теме, но тем не менее спрошу. Полгода назад, нужно было организовать синхронизацию времени на клиентской машине с сервером через java приложении. Кроме как сделать через JNI другого варианта не нашлось. Возможно ли это без вызова сишных функций?
Очень интересно, спасибо!
Жду продолжения как использовать C++ классы в JAVA.
Жду продолжения как использовать C++ классы в JAVA.
Полезные статьи про JNI:
How to Handle Java Finalization's Memory-Retention Issues — java.sun.com/developer/technicalArticles/javase/finalization/
Debugging integrated Java and C/C++ code — www-128.ibm.com/developerworks/java/library/j-jnidebug/index.html
Handling events from native objects in Java code — www-128.ibm.com/developerworks/java/library/j-jniobs/index.html
Signal Chaining — java.sun.com/javase/6/docs/technotes/guides/vm/signal-chaining.html
How to Handle Java Finalization's Memory-Retention Issues — java.sun.com/developer/technicalArticles/javase/finalization/
Debugging integrated Java and C/C++ code — www-128.ibm.com/developerworks/java/library/j-jnidebug/index.html
Handling events from native objects in Java code — www-128.ibm.com/developerworks/java/library/j-jniobs/index.html
Signal Chaining — java.sun.com/javase/6/docs/technotes/guides/vm/signal-chaining.html
Чистой воды юмор — «а в чём проблема? ставишь c# и полетел!»
Спасибо большое! Очень интересно прочитать продолжение.
Отличная статья. Жду продолжения!
Существует генераторы кода для подобных вещей. Для примера — SWIG (http://www.swig.org)
SWIG — инструмент для связывания программ, написанных на C/C++ с другими языками высокого уровня.
P.S. SWIG используется в Google для связывания C++ и Python.
SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages.
SWIG — инструмент для связывания программ, написанных на C/C++ с другими языками высокого уровня.
P.S. SWIG используется в Google для связывания C++ и Python.
Спасибо. Очень интересно и познавательно.
Если бы описали работу со структурами более подробно была бы хорошая статья, а так, данные информация есть в книжке Шилдта, которая и дает базовые понятия от jni.
UFO just landed and posted this here
У меня немного другая задача: есть сервис написанный на Java. Кроме всего прочего у него наружу торчит RMI с которым успешно работает приложение на C#. Нужно научиться работать из Linux/C++. Что посоветуете использовать?
Sign up to leave a comment.
Как подружить Java и C++. Часть первая