Я пытаюсь создать привязку библиотеки C к моему коду Java с помощью JNA, но у меня очень плохие результаты.
Вот заголовочный файл C
struct facet_fin_s {
int facet;
int fin;
};
typedef struct facet_fin_s facet_fin_t;
struct tab_facet_fin_s {
facet_fin_s *data;
int length;
};
typedef struct tab_facet_fin_s tab_facet_fin_t;
struct facet_s{
int number_of_fins;
tab_facet_fin_s tab_facet_fin;
};
typedef struct facet_s facet_t;
extern "C" __declspec(dllexport) void getFins(facet_t* const );
Вот файл С
void getFins(facet_t* const facet)
{
facet->number_of_fins = 258246;
facet->tab_facet_fin.length = facet->number_of_fins;
facet->tab_facet_fin.data = (facet_fin_s*)malloc(sizeof(facet_fin_s) * facet->tab_facet_fin.length);
memset(facet->tab_facet_fin.data, 0, sizeof(facet_fin_s) * facet->tab_facet_fin.length);
int loop = 0;
for (loop=0; loop<facet->tab_facet_fin.length; loop++)
{
facet_fin_s fin;
fin.facet = loop;
fin.fin = loop;
facet->tab_facet_fin.data[loop] = fin;
}
}
и, наконец, мой тест на Java
facet_s retFacet = new facet_s();
TestJNABindingLibrary.getFins(retFacet);
Structure facetFin[] = retFacet.tab_facet_fin.data.toArray(retFacet.tab_facet_fin.length);
for (int i = 0; i < facetFin.length; i++)
{
System.out.println(((facet_fin_s)facetFin[i]).fin);
System.out.println(((facet_fin_s)facetFin[i]).facet);
}
Результаты, возвращаемые моей функцией getFins, верны, но операция выполняется очень медленно. Я прикинул, что вызов "toArray" на retFacet.tab_facet_fin.data занимает 38 секунд!!
Я думаю, что JNA тратит слишком много времени на синхронизацию структур Java с нативной структурой и копирование данных.
Я попробовал массивы Byte и ByteBuffer для прямого доступа к памяти без копирования, но эти методы удобны для примитивных объектов, а не для структур. Я также пытался играть с указателями для легкого доступа к данным, но безуспешно.
Моя цель — найти способ улучшить производительность, сохраняя при этом понятный и простой в использовании код Java (у меня будет много таких функций в проекте). Есть ли способ добиться этого с помощью JNA? (Я уже рассмотрел JNI, SWIG и BridJ..). Некоторый код приветствуется ;-)
Спасибо
ИЗМЕНИТЬ
Вот моя попытка отключить автосинхронизацию и прочитать поле
facet_s retFacet = new facet_s();
retFacet.setAutoSynch(false);
TestJNABindingLibrary.getFins(retFacet);
facet_fin_s[] fins = (facet_fin_s[])retFacet.tab_facet_fin.readField("data");
к сожалению, fins
выглядит как null
ИЗМЕНИТЬ 2
Technomage сказал мне, что я должен сначала прочитать tab_facet_fin
. Но я все еще не могу получить результаты в виде массива.
tab_facet_fin_s tab = (tab_facet_fin_s)retFacet.readField("tab_facet_fin");
facet_fin_s[] fins = (facet_fin_s[])tab.readField("data");
вызывает исключение приведения. Есть ли простой способ прочитать это поле?
ИЗМЕНИТЬ 3
Благодаря Technomage я полностью опробовал стратегию readField
. Есть два способа получить данные, в зависимости от того, является ли data
Pointer
или Structure.ByReference
.
вот общая часть (каждый класс Java вызывает setAutoSynch(false)
в своем конструкторе)
facet_s retFacet = new facet_s();
TestJNABindingLibrary.getFins(retFacet);
затем дело Pointer
int length = (int)retFacet.readField("number_of_fins");
tab_facet_fin_s tab = (tab_facet_fin_s)retFacet.readField("tab_facet_fin");
int[] data = new int[length*2];
tab.data.read(0, data, 0, data.length);
for (int i = 0; i < data.length; i++)
{
System.out.println(data[i]);
}
или дело Structure.ByReference
.
tab_facet_fin_s tab = (tab_facet_fin_s)retFacet.readField("tab_facet_fin");
facet_fin_s s = (facet_fin_s)tab.readField("data");
facet_fin_s[] data = (facet_fin_s[])s.toArray(length);
for (int i = 0; i < data.length; i++)
{
System.out.println(data[i].fin);
System.out.println(data[i].facet);
}
Теперь мое мнение:
Стратегия
readField
может быть хорошим способом оптимизировать производительность и избежать бесполезного копирования. Это может быть хорошим приемом, но здесь он не актуален, потому что в моей структуре есть только те данные, которые я хочу прочитать. Если мои другие структуры в моем проекте содержат данные, которые я не хочу читать, я буду использовать их окончательно.Случай с указателем: к сожалению, JNAerator автоматически генерирует мой
data
какStructure.ByReference
, а неPointer
. Но давайте представим, что я получаю этиPointer
. Тогда я также могу очень быстро получить доступ к значениям int внутри данных. Если я не ошибаюсь, этот способ точно такой же, как вызовPointer.getIntArray
. Я вижу здесь 2 проблемы. Во-первых, я полностью теряю преимущество наличияfacet_fin_s
class в Java. Способ парсинга данных нормальный, но не очень удобный. Во-вторых, если моя структураfacet_fin_s
содержит члены другого типа (а это имеет место для некоторых структур библиотеки, которую я пытаюсь связать), то эта стратегия неуместна.Случай Structure.ByReference: здесь хорошо то, что мы получаем данные в виде массива
facet_fin_s
. Это хороший момент для читаемости кода. К сожалению, мы вернулись к первой проблеме, потому что нам нужно использовать этот проклятыйStructure.toArray
для доступа к данным. Эта функция создает копию памяти из собственной памяти в память Java. Для большого количества данных эта функция очень медленная.
Есть ли способ очень быстро прочитать данные из собственной памяти и сохранить исходную «архитектуру» без полного переписывания кода Java или C?
- Продолжайте использовать классы Java, представляющие структуры C
- Избегайте, насколько это возможно, переписывания множества инструментов или классов на Java или C, чтобы мы могли использовать только JNAerator.
- Быстрый и удобочитаемый доступ к собственной памяти или быстрое копирование из собственной памяти в память Java
Я думаю, что сталкиваюсь с ограничениями JNA...