我正在尝试使用 JNA 创建 C 库到我的 Java 代码的绑定,但我的性能很差。
这是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 );
这是C文件
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返回的结果是正确的,但是操作真的很慢。我认为在 retFacet.tab_facet_fin.data 上调用“toArray”需要 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
是 aPointer
还是 a 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
的 asStructure.ByReference
和 notPointer
。但是让我们想象一下我得到了这些Pointer
。然后我也可以非常快速地访问数据中的 int 值。如果我没记错的话,这种方式与调用Pointer.getIntArray
. 我在这里看到两个问题。facet_fin_s
首先,我完全失去了在 Java中拥有类的好处。解析数据的方式还可以,但不是很方便。其次,如果我的结构facet_fin_s
拥有其他类型的成员(我试图绑定的库的某些结构就是这种情况),那么这种策略是无关紧要的。Structure.ByReference 案例:这里的好处是我们将数据作为
facet_fin_s
. 这是代码可读性的一个好点。不幸的是,我们又回到了第一个问题,因为我们必须在这里使用这个该死Structure.toArray
的来访问数据。此函数创建从本机内存到 Java 内存的内存副本。对于大量数据,这个功能真的很慢。
真的有什么方法可以非常快速地读取本机内存数据并保持原始“架构”,而无需完全重写 Java 或 C 代码吗?
- 继续使用代表 C 结构的 java 类
- 尽可能避免用 Java 或 C 重写很多工具或类,以便我们只能使用 JNAerator
- 对本机内存的快速可读访问,或从本机内存快速复制到 Java 内存
我想我正面临着JNA的局限性......