gpt4 book ai didi

java - 在没有 toArray 的情况下提高 JNA 性能

转载 作者:太空宇宙 更新时间:2023-11-04 08:55:17 25 4
gpt4 key购买 nike

我正在尝试使用 JNA 创建 C 库到我的 Java 代码的绑定(bind),但我的性能很差。

这是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 结构与 native 结构同步并复制数据。

我尝试了 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 上进行了完整的尝试战略。有两种获取数据的方法,取决于 dataPointerStructure.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 确实会自动生成我的 dataStructure.ByReference而不是 Pointer .但是让我们想象一下我得到了这些 Pointer .然后我也可以非常快速地访问数据中的 int 值。如果我没记错的话,这种方式和调用 Pointer.getIntArray 完全一样.我在这里看到两个问题。首先,我完全失去了拥有 facet_fin_s 的好处。 Java 中的类。解析数据的方式还可以,但不是很方便。其次,如果我的结构 facet_fin_s拥有其他类型的成员(我试图绑定(bind)的库的某些结构就是这种情况),那么这个策略是无关紧要的。
  • Structure.ByReference 案例:这里的好处是我们将数据作为 facet_fin_s 的数组获取。 .这是代码可读性的一个好点。不幸的是,我们又回到了第一个有问题的地方,因为我们必须在这里使用这个该死的 Structure.toArray访问数据。此函数创建从 native 内存到 Java 内存的内存副本。对于大量数据,这个功能真的很慢。

  • 真的有什么方法可以非常快速地读取 native 内存数据并保持原始“架构”,而无需完全重写 Java 或 C 代码吗?
  • 继续使用代表 C 结构的 java 类
  • 尽可能避免用 Java 或 C 重写大量工具或类,以便我们只能使用 JNAerator
  • 对 native 内存的快速可读访问,或从 native 内存快速复制到 Java 内存

  • 我想我正面临着JNA的局限性......

    最佳答案

    您应该关闭结构内存的自动同步 ( Structure.setAutoSynch(false) )。然后您可以调用 Structure.readField(String) 仅根据需要访问感兴趣的领域。
    Structure.toArray()本身不会消耗那么多时间,但是将 native 内存同步到大量结构的 Java 字段最终会导致大量反射,这通常很慢。这将取决于所涉及的结构的数量以及每个结构中的字段数量(并且递归嵌套的结构引用会增加更多开销)。

    顺便说一句,您可以转换 Structure.toArray() 的结果直接到facet_fin_s[]所以你以后不必重复类型转换。

    如果您在许多结构中只有几个字段,并且需要访问所有这些字段,那么最好使用具有更好的 Java 到 native 传输性能的内存块表示(NIO 或原始数组)。无论使用什么平台,您都不想单独传输数千个字段。理想情况下,您希望将所有要传输的数据拉入单个缓冲区或数组并执行一次传输(Pointer.getIntArray() 可能适用于这种特殊情况)。

    编辑

    假设您的 data tab_facet_fin 内的字段是 Pointer 类型,然后您可以像这样提取数据:

    int[] buf = new int[LENGTH*2];
    tab_facet_fin.data.read(0, buf, 0, buf.length);

    相反,如果您映射 dataStructure.ByReference (即 struct* ),那么您需要执行以下操作:
    facet_fin_s s = (facet_fin_s)tab.readField("data");
    facet_fin_s[] data = (facet_fin_s[])s.toArray(LENGTH);

    请注意,您应该在要避免它的所有结构的 ctor 中设置 auto sync false ,以便在创建结构时自动发生。 Structure.toArray()来电 Structure.autoRead()返回之前在所有数组元素上。

    编辑 2

    一般来说,JVM 对任何类型的本地访问都不友好。进行单个 native 函数调用的开销很大。 Structure.toArray() 的实际开销正在逐个读取每个字段,每次读取都会导致 JNI 交叉。最好的解决方案是尽可能少地进行 JNI 转换,因此这意味着传输数据,然后将其分类到其组成部分中。

    如果您将所有内容都放在一个缓冲区中,您仍然可以使用 JNA 计算的信息来访问它。你可以想象自己制作 Memory由 native 内存支持的类,但经过优化以读取整个 native 内存块一次,然后覆盖所有 Pointer.getXXX访问 Java 端缓冲区而不是 native 内存的方法。这可能是 JNA 中的一个有用功能,并且可能是默认优化。缺点是你现在有两倍的内存使用量,所以它不一定总是最好的解决方案。

    注意:扩展 JNAerator 生成的接口(interface)以添加未配置为生成的映射是微不足道的。例如,如果它发出以下内容:
    interface MyLibrary extends Library {
    void myFunction(Pointer arg);
    }

    你可以像这样增加它:
    interface MyLibrary2 extends MyLibrary {
    void myFunction(MyStructure arg);
    }

    关于java - 在没有 toArray 的情况下提高 JNA 性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17442430/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com