我正在使用JNA封装一个本地代码(主要是Fortran 77)。原生函数的输出(即结果)由一堆嵌套的(自定义)类型结构组成,我将其映射到相应的 Structure
在JNA中。这些 Structures
大多是由一系列其他的 Structures
所以结构A持有结构B的数组,结构B持有结构C的数组等等)。
使用同样的基准测试(主要是通过记录时间差),我发现大部分时间不是花在原生代码中,而是花在JNA的映射过程中。Fortran子程序调用需要50ms左右,但总时间是250ms。
我发现
.setAutoWrite(false)
在我们 Structure
减少了2倍的开销(总执行时间几乎减半)。DOUBLE PRECISION
(double
)至 REAL
(float
)似乎没有任何区别在我们的情况下,有什么进一步的技巧来优化JNA性能吗?我知道我可以将我的结构扁平化为一个1D基元数组,并使用直接映射,但我尽量避免这样做(因为对这些结构进行编码会很麻烦)。
正如在 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)
. 这确实使用了反射,但你可以为每个结构做一次,并存储一个名称到偏移的映射,供以后使用。