JNA Fortran性能调整

问题描述 投票:1回答:1

我正在使用JNA封装一个本地代码(主要是Fortran 77)。原生函数的输出(即结果)由一堆嵌套的(自定义)类型结构组成,我将其映射到相应的 Structure 在JNA中。这些 Structures 大多是由一系列其他的 Structures 所以结构A持有结构B的数组,结构B持有结构C的数组等等)。

使用同样的基准测试(主要是通过记录时间差),我发现大部分时间不是花在原生代码中,而是花在JNA的映射过程中。Fortran子程序调用需要50ms左右,但总时间是250ms。

我发现

  • .setAutoWrite(false) 在我们 Structure 减少了2倍的开销(总执行时间几乎减半)。
  • 保持(静态分配的)数组尽可能小,有助于保持JNA的低开销。
  • 改变 DOUBLE PRECISION (double)至 REAL (float)似乎没有任何区别

在我们的情况下,有什么进一步的技巧来优化JNA性能吗?我知道我可以将我的结构扁平化为一个1D基元数组,并使用直接映射,但我尽量避免这样做(因为对这些结构进行编码会很麻烦)。

fortran jna
1个回答
1
投票

正如在 JNA常见问题直接映射将是你最好的性能提升,但你已经将其排除在选项之外。 它还指出,每个本地调用的调用开销是另一个性能影响,你已经通过改变 setAutoWrite().

你也提到了将你的结构扁平化为一个基元数组,但由于编码解码的复杂性,你拒绝了这个方案。 然而,朝这个方向发展可能是下一个最好的选择,你目前面临的最大性能问题可能是JNA的 Structure 使用反射和本地读取进行访问。 Oracle指出:

由于反射涉及的类型是动态解析的,因此无法进行某些Java虚拟机优化。 因此,反射式操作的性能比非反射式操作的性能要慢,在性能敏感的应用程序中,应该避免在代码中频繁调用的部分使用反射式操作。

既然你在这里问的是一个与性能有关的问题,而且使用的是JNA Structures,我只能假设你写的是一个 "性能敏感型应用"。 在内部,结构体是这样做的。

for (StructField structField : fields().values()) {
    readField(structField);
}

对每个字段进行一次Native读取,然后是这个,最后是使用反射的方法

setFieldValue(structField.field, result, true);

这个故事的寓意是,通常使用Structures,一般每个字段都会涉及到native读+反射写,或者反射读+native写。

在不做其他改动的情况下,你可以做的第一步是 setAutoSynch(false) 的结构上。 (你在 "写 "版本中已经完成了一半的工作,这个版本既能读也能写。) 从文档中可以看出,对于极其庞大或复杂的结构,如果你只需要访问少量的字段,你可能会看到一个 "写 "字段。

对于非常大或复杂的结构,你只需要访问少量的字段,通过避免结构的自动读写,你可能会看到显著的性能优势。如果禁用了自动读和-写,那么就需要你通过readField(String)和writeField(String,Object)来确保感兴趣的Java字段在本地函数调用之前和之后同步。当本地调用填充了一个大的结构,而你只需要其中的几个字段时,这通常是最有效的。在本机调用之后,你可以只对感兴趣的字段调用readField(String)。

要真正全力以赴,扁平化可能会更有助于摆脱任何反射开销。 诀窍是让偏移转换变得简单。

一些方向,平衡复杂性与性能。

  • 写入本机内存,分配和清除一个缓冲区的字节( )。mem = new Memory(size); mem.clear(); 或者只是 new byte[size]),并将特定的字段写入您使用来自 Structure.fieldOffset(name). 这确实使用了反射,但你可以为每个结构做一次,并存储一个名称到偏移的映射,供以后使用。
  • 对于从本机内存中读取,使用一个平面缓冲区进行所有的本机读取调用,以将本机开销减少到一次读写。 你可以在读取该缓冲区时将其投向一个Structure(每个字段都会招致一次反射),或者按照上述策略读取特定的字节偏移量。
© www.soinside.com 2019 - 2024. All rights reserved.