Взаимодействие Java и… Ассемблера?

В Java существует возможность использования программного кода, реализованного на других языках программирования, так называемый JNI. Можно написать динамически линкуемую библиотеку, затем загрузить ее в Java-коде и использовать функции оттуда, объявив их как native методы загрузившего ее класса. JNI создавался в первую очередь для того, чтобы выполнять машинно-зависимые действия (а также, возможно, улучшить производительность критических по скорости частей приложения) на C/C++, но никто не мешает нам написать библиотеку и на ассемблере.

Инструментарий


Для создания сего извращения применялись Windows, Oracle JDK, Yasm (без макросов) и линковщик из Microsoft Visual Studio 2010. При желании, заменить любой из этих компонентов на ваш любимый не составит большого труда, я старался не использовать всякие нестандартные фичи.

Использование JNI


Для начала, создадим на Java класс, в котором будет вызываться функция из будущей .dll'ки:

public class TestJNI {    
    native static int sum(int x, int y);    // импортируемая функция sum
    
    public static void main(String[] args) {
        System.loadLibrary("mydll");        // загружаем библиотеку mydll.dll
        System.out.println(sum(2, 3));      // вызываем функцию
    }
}


Name Mangling в Java


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

javac TestJNI.java
javah TestJNI


При этом будет сгенерирован C++ header:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestJNI */

#ifndef _Included_TestJNI
#define _Included_TestJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     TestJNI
 * Method:    sum
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_TestJNI_sum
  (JNIEnv *, jclass, jint, jint);

#ifdef __cplusplus
}
#endif
#endif


Информация, содержащаяся здесь, нужна главным образом для дальнейшего написания библиотеки на C++, нас же интересует только сигнатура функции:

JNIEXPORT jint JNICALL Java_TestJNI_sum
  (JNIEnv *, jclass, jint, jint);


Мы видим, что в dll функция должна иметь имя JNICALL Java_TestJNI_sum и принимать 4 параметра. Для простейших функций первые два из них нам не понадобятся. Всякие странные типы данных вроде JNIEXPORT, как несложно догадаться, объявлены в файле jni.h, который входит в состав JDK.

Ассемблер


Напишем теперь библиотеку:

;mydll.asm
section .text

global Java_TestJNI_sum
    
Java_TestJNI_sum:
    mov eax, [esp + 12] ; игнорируем первые 2 параметра
    add eax, [esp + 16]
    ret 16
    
end


;mydll.def
LIBRARY mydll
EXPORTS Java_TestJNI_sum


Скомпилируем и слинкуем:
yasm -f win32 mydll.asm
link /SUBSYSTEM:windows /DLL /NOENTRY /DEF:mydll.def mydll.obj


На выходе получим .dll-файл, который и нужно передавать коду на Java.

Результат


Положим файлы mydll.dll и TestJNI.class в одну папку и посмотрим, что получилось:

>java TestJNI
5


Это победа, мы научились складывать два числа!

Если вдруг кому-то стало интересно, то ему пригодятся следующие ссылки:

Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 92
  • +5
    Прошу прощения, конечно, но зачем это надо?
    • +3
      Ускорение работы при выполнении некоторых операций, выполнение некоторых машинно-зависимых операций, выполнение операций, недоступных для ЯВУ — вот они, причины.
      • +2
        То есть вы всерьёз планируете превзойти современный оптимизирующий С/C++ компилятор? Должно быть вы очень хорошо знаете ассемблер.
        • +7
          В некоторых случаях, это таки помогает. Даже если человек не очень хорошо знает ассемблер.
          Яркий пример — x264, в предыдущем Google Summer of Code новички написали несколько патчей, переписывая функции на ассемблер, что ускорило работу.
          Да и сами создатели x264 ругаются на компиляторы (в частности, gcc), которые плохо оптимизируют некоторые вещи.
          • –2
            Хорошо, может быть.
            Но, тем не менее, мне кажется, что в условиях коммерческой разработки софта, большее значение имеет скорость разработки конечного продукта и стоимость его дальнейшего сопровождения, нежели пара десятков сэкономленных тактов. Совсем уж специфические ситуации и задачи не рассматриваем, где как-раз требуется пресловутая микро-оптимизация.
            • +6
              Сам спросил, сам ответил.
              • –6
                Простите, не понял.
              • +2
                Почему же не рассматриваем? Вроде как только для этого и нужно.
                Возможных применений вижу два:
                1. Высокая производительность участка кода (например, специфичное шифрование/хэширование)
                2. Низкоуровневое общение с железом (особенно не на PC)
                • +3
                  По поводу пункта №1 есть сомнения, мне почему то помнится, что сам факт обращения к JNI является весьма ресурсоёмкой операцией, так что выносить туда небольшие участки будет невыгодно. А если выносить что-то большое, то вообще какой смысл в Java приложении?
                  • 0
                    Когда-то на смартфоне с Android замерял скорострельность вызовов JNI. Не помню, был там JIT или нет, но порядка миллиона вызовов в секунду стабильно делалось. А ресурсоёмкость вызова не столь велика, как кажется — ведь, насколько я помню, даже никаких локов не задействуется во время JNI-вызова.
                    • 0
                      Всё таки dalvik не совсем JVM, так что не очень корректно сравнивать.
                      А оверхед JNI возникает из-за того, что используются разные области памяти, между которыми нужно переносить данные.
                • –4
                  «в условиях коммерческой разработки софта, большее значение имеет скорость разработки конечного продукта и стоимость его дальнейшего сопровождения, нежели пара десятков сэкономленных тактов.»

                  +100
                  • 0
                    Расскажите это авторам тех самых видеокодеков, прозрачных шифровальщиков трафика (под VPN давно видели последний раз?), а ещё Кармаку — он в своё время Fast Inverse Square Root, наверное, только для удобства сопровождения использовал вместо 1.0 / sqrt(x).
                    • 0
                      Да, есть еще софт, управляющий крылатыми ракетами.

                      Но в большинстве областей разработки важнее время разработки и стоимость труда программистов
                      • 0
                        А я с этим и не спорил, между прочим. PATOGEN написал:
                        Ускорение работы при выполнении некоторых операций, выполнение некоторых машинно-зависимых операций, выполнение операций, недоступных для ЯВУ — вот они, причины.

                        Я всего лишь примеры привёл, что не всегда "пара десятков сэкономленных тактов" того не стоит.
              • –3
                «вы всерьёз планируете превзойти современный оптимизирующий С/C++ компилятор» это уже становится нормой размышлений, как будто люди зашли в тупик своего развития и производить что то лучшее могут только машины. Печаль.
                • +1
                  Компиляторы, по-вашему, тоже машины пишут?
                  Нет, тут дело в другом. Тут дело в ошибочной уверенности, что «команда специалистов-разработчиков компилятора — идиоты, я в одиночку смогу лучше».
                  А вообще, холиваров «компилятор vs ассемблер» навалом, предлагаю не начинать ещё один и каждому остаться при своём мнении. :)
                  • 0
                    Я много раз попадал в ситуации, когда хитрая перестановка команд в C++ коде и ручной constant propagation ускорял код на десятки процентов, притом что gcc 4.4.1 с O3 и прочими жестокими флагами и MSVCC с настройкой на аггресивную оптимизацию не могли ничего сделать. Само собой, код вычислительный, а не т.н. «бизнес-логика».
                    • –2
                      Вот и я о том же, печально что сейчас масса разработчиков уверена в магии компиляторов на столько, что мысли о возможности сделать лучше отсекаются в зачаточном состоянии.
                      • +3
                        Просто не нужно думать, что компилятор сделает всю работу за программиста/проектировщика. Его удел — оптимизировать тонкие места с учётом машинной архитектуры, а не, видя пузырьковую сортировку, бросаться менять её на quicksort. А это значит, что думать во время написания кода таки надо.
                    • 0
                      Интересная статья, спасибо.
                      • 0
                        А указано с какими настройками компилился код? Правильно настроенный компилятор и дефолтные настройки — это разные оптимизации.
                        З.Ы. Я полагаю что на вопрос человек против компилятора нет однозначного ответа.
                        • +1
                          Я смотрю, комментарии вы не читали.
                      • –2
                        В нашем игровом проекте, есть конкретная задача, считающая бой юнитов.
                        Основная проблема — индивидуальный просчет атаки и защиты каждого юнита (упрощать алгоритм не хотелось)
                        При просчете боя в миллионы юнитов, проблема выделения памяти и элементарных математических вычислений стала критичной (несколько часов — один бой). После написания библиотеки на ассемблере, средний бой считается доли секунды, крупные — несколько секунд.
                        Си, как и любой другой язык высокого уровня, достаточно много времени тратит на выделение памяти под хранение переменных.
                        • 0
                          При просчете боя в миллионы юнитов, проблема выделения памяти и элементарных математических вычислений стала критичной (несколько часов — один бой). После написания библиотеки на ассемблере, средний бой считается доли секунды, крупные — несколько секунд.

                          несколько часов -> несколько секунд

                          То есть вы написали код, минимум в полторы-две тысячи раз эффективней, чем тот, что сгенерировал компилятор?
                          • –1
                            Не понимаю, почему вас это удивляет. Ассемблер работает с памятью быстрее чем откомпилированный код, ибо текст на ассемблере можно оптимизировать буквально по тактам.
                            Задача стояла достаточно простая — большая куча данных и 4 элементарных математических действий.
                            • +4
                              Нет, ну я понял бы прирост в скорости в 300-500%, но в 200000% — это да, это меня удивляет.
                              • –2
                                Ну, возможно, разница с откомпилированным Си была бы меньше.
                                Но ява это же байт-код, а не машинный код, а писать алгоритм на разных языках, если предварительно было видно, что мы можно это за пару дней сделать на асме не было смысла, лучше сразу.

                                Вдобавок основной топик — взаимодействие явы и ассемблера, а не явы и Си, я привел практический пример, который позволил десяткам тысяч игроков жить веселее.

                                P.S. Просьба минусовать с аргументами.
                                • +5
                                  Когда я вижу заявления типа «ява это же байт-код, а не машинный код», мне кажется, что человек не очень хорошо разбирается в jvm. Производительность java приблизительно равна производительности c++ (где-то чуть лучше, где-то чуть хуже). Поэтому 200000% на самом деле звучат по меньшей мере дико и, скорей всего, существуют ошибки в java коде (пример из практики: чел, вышедший с с++, использовал Vector вместо List, и недоумевал, почему программа работает медленно).
                                  • 0
                                    Сразу оговорюсь, что память jvm жрет много — тут я даже оправдывать ее не буду. Поэтому если у вас просадки из-за того, что вы не даете ей памяти вдоволь, то тут уж никак ситуацию не исправишь.
                                    • +2
                                      Я же явно указал, что основная проблема — объем данных, то есть память, а операции простейшие, зачем цепляться к другим словам?

                                      По сути, основная доля времени уходит на:
                                      1. В бою может участвовать несколько миллионов юнитов по обе стороны баррикады, у каждого юнита есть несколько параметров, которые меняются в процессе боя (щиты, броня, статус мертв/жив), на это нужно выделить память и быстро к ней обращаться.
                                      3. Случайный выбор очередной жертвы при каждом выстреле, включая грамотный выбор очередной жертвы, если текущая уже мертва, и сортировка оставшихся живых.

                                      В нашем конкретном случае это помогло избежать не только проблемы нагрузки на сервере, но так же и то, что игроки, среди которых есть программисты (ну ладно, может не профессиональные), даже после полного описания алгоритма, пытались и пытаются его воспроизвести на привычных им языках программирования, но на текущий момент, я не знаю ни одной попытки сделать это на ассемблере.

                                      Текущие эмуляторы просчитывают бои гораздо дольше нашего (несколько минут то, что у нас секунды), в результате, учитывая случайную составляющую — для прогноза результата боя, было бы хорошо его прогнать раз 10-100 и затем взять уже среднее, на что уходят часы (прогнозируют обычно для самых крупных сражений).

                                      P.S. Еще раз хочу сказать, что я просот привел пример удачного взаимодействия ассемблерной вставки для веб-проекта на практике. Спорить о том, что круче — компилятор или ассемблер я не хочу.
                                      • +1
                                        >> Основная проблема — индивидуальный просчет атаки и защиты каждого юнита (упрощать алгоритм не хотелось)

                                        То есть вы хотите сказать, что оставив достаточно сложный алгоритм полностью как есть, без изменения его структуры переписали его с языка высокого уровня на асм? Можно статью на хабре с мастер-классом, я бы с большим удовольствием почитал.

                                        Простая игра с типами данных (boxing-unboxing, ArrayList-LinkedList, HashMap — хеш для примитивных типов из того же colt) может дать хороший прирост, но без изменения самой логики (не обязательно упрощать, достаточно отбрасывать заранее ненужные рассчеты) я просто НЕ ВЕРЮ, что вы сделали это:

                                        >> (несколько часов — один бой). После написания библиотеки на ассемблере, средний бой считается доли секунды, крупные — несколько секунд.

                                        Индивидуально просчитать действия для каждого юнита, а их несколько миллионов за каждую сторону, отсортировать результат выживших (а он после каждого из хода тоже на миллионы) и уложиться в несколько секунд.

                                        Я знаю что у явы бывают узкие места, но тут кто-то откровенно врет.

                                        p.s. а можно описание этого алгоритма? просто для саморазвития
                                        • +1
                                          хотя, извиняюсь что не увидел этого:

                                          >> Задача стояла достаточно простая — большая куча данных и 4 элементарных математических действий.

                                          В этом случае да, mmx и sse вам в руки, но уж как-то смутило про грамотный выбор, сортировку и индивидуальный подход к каждому юниту
                                          • +2
                                            Основная суть — на ассемблере вместо работы с типами данных, просто выделяется область памяти и с юнитами работали просто по смещению адреса. Это действительно быстрее, чем работа с хэшами и массивами.

                                            После окончания одного раунда, делаем дефрагментацию, оставляя только живых юнитов для более адекватного рандомайза — если рандомайзер выпадает на уже убитого в раунде юнита, вместо вычисления еще одного рандома (который опять может попасть на убитого), просто выбирается следующий юнит. Если убито 10 юнитов, следующих подряд, вероятность попадения в 11 юнит значительно увеличивается, но дефрагментировать чаще — большой расход времени.

                                            В результате — в цикле у нас остались однотипные операции, которые можно оптимизировать буквально потактово, увеличив эффективность общего цикла.

                                            Вот поискал по нашему форуму… Официальный лог боя — расчет длился около 20 секунд на сервере (двухпроцессорный intel)
                                            Имеющийся симулятор1 на Си, написанным игроком, (домашний целерон) считает от 10 до 20 минут (много рандома).
                                            Имеющийся симулятор2 на яве, написанный игроком, (домашний целерон) завис.

                                            В бою участвовало примерно 2.5 миллиона юнитов.

                                            P.S. Еще раз хочу сказать, что совершенно не хочу сказать что у нас какое-то уникальное достижение, просто удачно повезло с самой задачей. При открытии проекта существование подобного размера армий вообще не предполагалось, но не рассчитали с экономикой…
                                            • 0
                                              > Основная суть — на ассемблере вместо работы с типами данных, просто
                                              > выделяется область памяти и с юнитами работали просто по смещению
                                              > адреса. Это действительно быстрее, чем работа с хэшами и массивами.

                                              что мешает сделать тоже самое на той же java?
                                              • 0
                                                1) То, что это уже сделано на ассемблере.
                                                2) видимо на ассемблере разработчик шарил больше, чем на яве.

                                                Меня не радует, что многие комментаторы рассуждают об экономии времени программиста, но если что-то уже написано и работает, предлагают это все переделать просто потому, что это можно сделать иначе.
                                                Много ли хороших и грамотных программистов действительно все переделывают в уже работающих проектах так, как они рекомендуют?

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

                                                P.S. Если у вас есть свободное время, я могу кинуть детальный алгоритм боевки, напишете его на java, сравним скорость. Не то, чтобы сейчас было важно что оно будет считаться считается 5 секунд или 6, но все же я не думаю, что ява сможет обогнать ассемблер в конкретно этой задаче.
                                                • +1
                                                  Я вам не предлагаю ничего переделывать, раз оно УЖЕ работает. Но вот если бы я только собирался переписать некоторый участок кода я бы хорошо подумал прежде чем использовать ассемблер. Потому что поддерживать ассемблерный код гораздо сложнее.

                                                  Насчет свободного времени не уверен — только между компиляцией, но было бы интересно, только на с++: с java я знаком слишком поверхностно для того, чтобы заниматься оптимизаторством.
                                              • 0
                                                Если убито 10 юнитов, следующих подряд, вероятность попадения в 11 юнит значительно увеличивается
                                                Отнюдь, вероятность остаётся 50%… Ну при прочих равных. Если вы знаете, что у вас 20 из ста убито, и уже 10 нашли, тогда конечно увеличивается.
                                                • 0
                                                  есть 10 юнитов, убиты 4,5,6,7,8.
                                                  Сравните вероятность попадания в 3 юнит и в 9-й, учитывая что попадания в мертвые 4,5,6,7,8 также приведут к попаданию в 9-й.

                                                  Более красивый подход, который не злоупотребляет ресурсами, как дефрагментацию после окончания каждого раунда, пока не нашли…

                                                  Но всегда готовы прислушаться :)
                                                  • 0
                                                    Ай, я не до конца понял условие. Тут вы конечно правы.
                                  • +2
                                    А что за игра, если не секрет?
                            • 0
                              Весь выигрышь в скорости съест очень высокий overhead JNI.
                            • +1
                              Например, можно выполнить какие-нибудь SSE-инструкции для оптимизации обработки данных.
                              • –1
                                А как же переносимость на другие платформы? А если о ней не идёт речи, тогда зачем Java?
                                • –1
                                  А для остальных платформ использовать условную компиляцию/линковку, тогда будет выполняться обычный байткод.
                                • 0
                                  В вычислительной задаче переносимость часто может быть не нужна — код работает на единственной машине или одном виде машин.
                                  • +1
                                    В вычислительной задаче будет довольно-таки странно использовать Java. Не забывайте также об оверхеде на JNI.
                                    • 0
                                      Сложно представить какой-то другой тип задач, в котором есть потребность в JNI. Все-таки Java иногда используется для вычислений. (Помнится кто-то на хабре рассказывал про портирование вычислительного C++-кода на Яву!)

                                      Если 90% процентов процессорного времени приходится как раз на нативные вставки, JNIшный оверхед перестает играть роль.
                                  • 0
                                    По-моему, тут был чисто академический интерес. Особой смысловой нагрузки этот пример не несет.

                                    А можете замеры времени провести?
                                    • +1
                                      Ассемблерную вставку можно использовать как кросс-платформенный плагин к php/apache/etc
                                      Ну и не всегда необходима переносимость в виде «взял и перенес как есть». А с небольшими правками, правильно написанную библиотеку можно скомпилировать под нужную платформу.
                                      • 0
                                        Сейчас есть популярные серверные платформы, работающие не на x86/x64?
                                        • 0
                                          А кто сказал, что речь идёт лишь о серверных платформах?
                                    • +5
                                      Оптимизация узких мест? Хотя, наверное, C/C++ будет уместнее — по слухам они оптимизируют во многом лучше «гуру» ассемблера.
                                      • +1
                                        Получается, что самое выгодное — линковка с C[++] со вставками ассемблера. Как собственно и без явы.
                                      • +1
                                        Материал из данной статьи очень поможет, при подключении нативных библиотек в android приложениях.
                                      • +1
                                        Вот честно — я сильно сомневаюсь, что писать в чистую на asm надо, если у вас конечно не идет там работа с хитрым железом. JIT все с оптимизирует сама и там где надо превратит в ассемблерный код. Даже C++ код намного лучше (оптимальней) преобразуется в асм компилятором, чем вручную.
                                        • 0
                                          Скорее новым железом :)
                                          Инструкция PCMPESTRI позволяет strcmp-нуть 16-символьные строки за 4 (!!!) такта. А GCC, и уж тем более явовский JIT ее и подобные вообще не используют.
                                          • +2
                                            hg.openjdk.java.net/hsx/hotspot-main/hotspot/rev/fbde8ec322d0 — доказывает, что либо уже используется, либо работы ведутся в openJdk7, а значит, в Oracle jdk и подавно. JVM рулит, на самом деле )
                                            • НЛО прилетело и опубликовало эту надпись здесь
                                            • 0
                                              Мне кажется, они не используют их для того, чтобы откомпилированный код можно было запустить на отличных от компилирующей машинах (не везде ведь есть SSE4). Вот, например, в Intel Compiler есть отдельные опции «Insert Intel-optimized code path» и «Perform compiling host specific optimizations» (за дословность не ручаюсь — нет компилятора под рукой), которые такими вещами не пренебрегают (сам видел, читая assembler output).
                                              • 0
                                                Теоретически код на Java лучше C++ уже тем фактом, что вы можете скомпилировать на С++ код жестко оптимизированный компилятором например под Intel, а оптимизацию для AMD вам прийдется делать отдельно. JIT же в теории сама определяет архитектуру и те куски кода которые захочет перевести в asm код сделает весьма оптимальными.
                                                • 0
                                                  Ну в теории JIT, конечно, более мощная штука в плане оптимизаций под целевую машину. Но на практике для генерации действительно оптимизированного кода требуется слишком много времени и слишком сложный анализ, реализация логики которого неприлично увеличит объём программы-JIT компилятора.
                                                  Мне очень нравится схема, по которой работает JIT в платформе .NET — все программы JIT-компилируются не при первом запуске, а при установке на целевую машину (в идеале). И как раз в ngen.exe можно встроить очень мощные механизмы оптимизации и анализа кода, что при включении всего этого хозяйства в состав ОС (лишние пару сотен мегабайт вряд ли сильно повлияют на размер современной Windows =)) даст практически ту самую идеальную заточенность, о которой Вы говорите.
                                                  Кстати, теоретически можно создать объектные файлы с жёсткими оптимизациями под различные типы процессоров, а также «общую» версию. Скажем, под Intel и AMD компилируем своими компиляторами, а для общего случая — gcc. Затем либо выносим код в разные DLL с выбором нужной в рантайме, либо просто оформляем как различные секции в рамках одного бинарника (естественно, этим должна заниматься специальная программа или скрипт, а не человек) — всё равно большую часть объёма современных десктопных и смартфонных программ занимает далеко не программный код =). Кстати, такой механизм вовсю используется в играх под Android, использующих нативный код: просто в ресурсы кладутся версии нативных библиотек под разные версии ARM, а нужную выбирает JVM в момент загрузки.
                                                  • +1
                                                    Но на практике для генерации действительно оптимизированного кода требуется слишком много времени и слишком сложный анализ, реализация логики которого неприлично увеличит объём программы-JIT компилятора.

                                                    Вы так говорите, словно разрабатываете JIT-компиляторы для java. Только вот ведь незадача: вы их не только не разрабатываете, а ещё и не знаете, что умеют уже существующие.
                                                    • 0
                                                      Нет, я не занимаюсь разработкой JIT-компиляторов. А Вы, видимо, никогда в глаза не видели возможностей того же Intel Compiler и не пытались выжать из кода заветные проценты производительности ручной перестановкой инструкций, разворотом циклов и profile-guided optimization — ведь «JIT всё сделает, Intel — в помойку».
                                                      Привожу конкретный пример: один и тот же код софтварного рендеринга сцены из 27 текстурированных кубов в разрешении 480*800 (могу код скинуть, коль не верите на слово) имел разницу времени выполнения примерно в 2.5-3 раза между JIT-выполнением (с прогревом, разумеется) и банальным компилированием g++ с O3. Устройство — Samsung Galaxy S.
                                                      Если не верите тому, что я говорю (а я думаю, что не верите — правы-то, очевидно, Вы), предлагаю Вам, например, написать какое-нибудь умножение матриц на Java (или любой другой, интенсивно работающий с памятью, код) и обогнать хотя бы приблизиться по времени выполнения к написанному мной на коленке коду, скомпилированному g++. Готовы?
                                                      О ужас, как же я мог подумать, что какой-то там g++ может даже рядом стать по качеству оптимизирования числодробилки к самой HotSpot?!
                                                      • +1
                                                        Нет, я не видел возможностей Intel Compiler.
                                                        А я разве говорил, что код, сгенерированный JIT, всегда быстрее того, что вы вручную напишите и/или скомпилируете g++? (ответ: нет, не говорил).
                                                        И ежу понятно, что можно с лёгкостью найти пример, в котором JIT будет проигрывать. Тому же ежу понятно, что можно с лёгкостью найти и обратный пример.

                                                        Я лишь очень сильно раздражаюсь, когда люди с умным видом рассуждают на темы, с которыми слабо знакомы (например, то, как работают современные JIT-компиляторы), и потому сделал вам замечание. Прошу меня простить, если оно оказалось в слишком грубой форме.
                                          • +2
                                            Возможно данный вариант подойдет тем, у кого есть написанный модуль на ассемблере, выполняющий какие-либо вычисления, который жуть как не охота реализовывать на другом языке, и котороый хочется использовать к примеру в веб-проекте.
                                            • +1
                                              Да… Идет активная попытка вернуть вилки, ножи и веревки в Java. :-)

                                              Если так критично выполнять некие действия на машинно специфичном уровне, делать это быстро, то зачем оборачивать это в jni.

                                              Нельзя ли сделать отдельный сервис и общайтесь с ним как нибудь. По крайней мере не будете зависеть от заморочек виртуальной машины, она ж не только ваш код выполняет, ей еще мусор собирать и другими делами заниматься.
                                              • НЛО прилетело и опубликовало эту надпись здесь
                                              • +1
                                                ИМХО, эта возможность нивелирует идею явы о кроссплатформенности.
                                                • –1
                                                  JNI нивелирует это по определению.
                                                  • 0
                                                    Вовсе нет. При разумном использовании, разумеется.
                                                    • –2
                                                      Ну ежу понятно, что

                                                      printf("%d", 123)
                                                      


                                                      на всех платформах будет работать одинаково.

                                                      Я лично использую jni для работы с криптографией, и никакой кроссплатформенности там на С++ не будет, потому что апи у всех систем для этих дел свои.
                                                      • 0
                                                        Совершенно прав Damaskus. С JNI «write once, run anywhere» превращается в «write once, compile everywhere, run anywhere». При этом далеко не факт, что этап «compile everywhere» завершится успехом.
                                                  • +3
                                                    «10 способов писать непереносимый и привязанный к одной платформе код на Java.»
                                                    • +1
                                                      Ну что? Следующая статья — как собрать в один екзешникобьектник из gcc и masm?
                                                      • НЛО прилетело и опубликовало эту надпись здесь
                                                        • +5
                                                          Ну почему же, если эту конструкцию запустить под Linux:
                                                          System.loadLibrary("mydll");
                                                          то будет загружена библиотека libmydll.so
                                                          Таким образом, если в пакете установщика иметь одновременно
                                                          mydll.dll и libmydll.so то будет вполне себе даже кроссплатформенно.
                                                        • +1
                                                          С таким успехом можно написать даже статью «Взаимодействие 1С и ассемблера»))
                                                          Я думал, честно говоря, тут что-то необычное, а не просто подключение внешней библиотеки…
                                                          • –1
                                                            По информации из достоверных источниковтм, JNI тормозит, так что… Вывод можете сделать любой :P
                                                            • –1
                                                              Ваши источники врут. Я год назад демку писал под Андроид, и JNI просто летал, делая около миллиона чистых вызовов в секунду.
                                                              • 0
                                                                Да я пошутил, в общем-то. Кстати довольно интересно, как JNI оптимизируется в dalvik.
                                                                • –1
                                                                  По-моему, там нечего особо оптимизировать — нужно всего лишь передать указатель на контекст JVM (он и так в Dalvik нативный — контекст, в смысле) и дёрнуть [возможно, виртуальный] метод.
                                                                  • 0
                                                                    Как это нечего? Научить jit инлайнить native методы — это здорово бы было. В хотспоте ещё, например, при вызове нативных методов разные вещи творятся. Буквально вчера на jugru на этот вопрос отвечали TheShade и Walrus.
                                                                    • –1
                                                                      Вряд ли это возможно, насколько я понимаю. Ведь тогда есть риск здорово облажаться с reflection. Ведь его средствами можно получить байткод метода, и если в него JIT'ом будет подмешиваться что-то ещё, то можно, например, получить рассинхронизацию данных (если JIT заинлайнит нативный вызов после того, как через reflection был колучен его байткод).
                                                                      Далее, связывание native методов через JNI — позднее, а это значит, что метод может быть обработан JIT-ом до реальной загрузки соответствующей нативной библиотеки (простейший пример: библиотека загружается в теле этой же функции), а если «учить» JIT перекомпилировать методы, то это здорово скажется как на его ресурсоёмкости, так и на производительности.
                                                                      Третий мой аргумент против инлайнинга — если у вас настолько часто используется нативная функция внутри вашей (и это, разумеется, является «бутылочным горлышком»), может, стоит просто перенести саму функцию в натив?
                                                                      • 0
                                                                        Расскажите, пожалуйста, как через Reflection получить байт-код метода. Вы точно не перепутали с Instrumentation?

                                                                        Расскажите, пожалуйста, какой байт-код вы ожидаете получить у native метода? А то мне как-то казалось, что раз метод нативный, то у него нет байт-кода.

                                                                        $ echo "public class Foo { native void foo(); }" > Foo.java
                                                                        $ javac Foo.java && javap -c Foo
                                                                        Compiled from "Foo.java"
                                                                        public class Foo extends java.lang.Object{
                                                                        public Foo();
                                                                          Code:
                                                                           0:	aload_0
                                                                           1:	invokespecial	#1; //Method java/lang/Object."<init>":()V
                                                                           4:	return
                                                                        
                                                                        native void foo();
                                                                        
                                                                        }


                                                                        А вы вообще про какой JIT говорите? hotspot-овский, между прочим, проводит оптимизации только после того, как у него наберётся достаточно статистики по его использованию (что подразумевает, что динамическая библиотека будет загружена, если будет решено заинлайнить метод из неё). И да, hotspot-овский JIT умеет перекомпилировать методы (особенно если вспомнить про tiered compilation).

                                                                        Последний аргумент может быть состоятельным, хотя, очевидно, далеко не во всех случаях.
                                                                        • 0
                                                                          1) Например, вот. Или про Javassist гляньте. Странно, оба работаем в «Яндексе», но поиском Вы почему-то пользоваться так и не научились. Беда.
                                                                          2) Читайте внимательно (особенно перед тем, как оголтело минусовать):
                                                                          … можно получить байткод метода, и если в него JIT'ом будет подмешиваться что-то ещё, то можно, например, получить рассинхронизацию данных (если JIT заинлайнит нативный вызов после того, как через reflection был колучен его байткод).

                                                                          Код обычный, джавовский, в котором есть вызов нативного метода.
                                                                          3) Я говорю про любой JIT, который проводит компиляцию кода в рантайме (ещё раз прочитайте второй абзац моего предыдущего сообщения — там не зря про производительность написано)
                                                                          • 0
                                                                            1) Ни BCEL, ни Javassist, не используют reflection. Они оба просто вычитывают содержимое класс-файлов из classpath и разбирают его в байт-код. С одним местом работы оказия, конечно, но что ж поделаешь.

                                                                            2) Ок, тогда всё ещё хуже.
                                                                            байткод метода, и если в него JIT'ом будет подмешиваться что-то ещё

                                                                            JIT ничего не подмешивает в байт-код. Он его вообще не изменяет. Инлайнинг происходит в сгенерированном нативном коде.

                                                                            3) Тогда вы прочтите ещё раз то, что написано про hotspot. Вроде как, он относится к «любой JIT, который проводит компиляцию кода в рантайме» и «научен» перекомпилировать методы, однако на его производительность никто не жалуется. Более того, IBM VM тоже умеет. Более того, как я понял (хотя тут я не очень уверен, инфы по нему маловато, нужно читать исходники), dalvik тоже умеет.

                                                                            Да и вообще, любой JIT-компилятор, который поддерживает целевые оптимизации, основанные на профиле использования, очень просто «научить» перекомпилировать методы.

                                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.