JNA Fortran 性能调优
JNA Fortran performance tuning
我正在使用 JNA 包装本机代码(主要是 Fortran 77)。本机函数的输出(即结果)由一堆嵌套(自定义)types/structs 组成,我将其映射到 JNA 中相应的 Structure
。这些 Structures
主要由其他 Structures
的数组组成(因此结构 A 包含结构 B 的数组,结构 B 包含结构 C 的数组等)。
使用相同的基准测试(主要是通过记录时差)我发现大部分时间不是花在本机代码上,而是在 JNA 映射期间。 Fortran 子程序调用大约需要 50 毫秒,但总时间为 250 毫秒。
我发现
.setAutoWrite(false)
在我们的 Structure
上减少了大约 2 倍的开销(总执行时间几乎减半)
- 保持(静态分配的)数组尽可能小有助于保持较低的 JNA 开销
- 将
DOUBLE PRECISION
(double
)更改为REAL
(float
)似乎没有任何区别
在我们的案例中,是否还有其他技巧可以优化 JNA 性能?我知道我可以将我的结构扁平化为一维基元数组并使用直接映射,但我尽量避免这种情况(因为 encode/decode 这些结构会很痛苦)
如 JNA FAQ 中所述,直接映射将是您最好的性能提升,但您已将其排除在外。它还指出,每个本机调用的调用开销是另一个性能损失,您已通过更改 setAutoWrite()
.
部分解决了这个问题
您也确实提到了将您的结构扁平化为基元数组,但由于 encoding/decoding 复杂性而拒绝了。但是,朝着这个方向前进可能是下一个最佳选择,您当前面临的最大性能问题可能是 JNA Structure
使用反射访问和本机读取的组合。 Oracle notes:
Because reflection involves types that are dynamically resolved,
certain Java virtual machine optimizations can not be performed.
Consequently, reflective operations have slower performance than their
non-reflective counterparts, and should be avoided in sections of code
which are called frequently in performance-sensitive applications.
既然你在这里问一个与性能相关的问题并使用 JNA 结构,我只能假设你正在写一个 "performance-sensitive application"。在内部,结构是这样做的:
for (StructField structField : fields().values()) {
readField(structField);
}
它为每个字段执行一次本机读取,然后是这个,最终在引擎盖下使用反射。
setFieldValue(structField.field, result, true);
故事的寓意是,通常使用结构,通常每个字段都涉及本地读取+反射写入,或反射读取+本地写入。
您可以在不进行任何其他更改的情况下进行的第一步是 setAutoSynch(false)
结构。 (你已经用 "write" 版本完成了一半;它既读又写。)来自文档:
For extremely large or complex structures where you only need to
access a small number of fields, you may see a significant performance
benefit by avoiding automatic structure reads and writes. If auto-read
and -write are disabled, it is up to you to ensure that the Java
fields of interest are synched before and after native function calls
via readField(String) and writeField(String,Object). This is typically
most effective when a native call populates a large structure and you
only need a few fields out of it. After the native call you can call
readField(String) on only the fields of interest.
为了真正全力以赴,扁平化可能有助于消除任何反射开销。诀窍是使偏移转换变得容易。
一些方向,平衡复杂性与性能:
- 要写入本机内存,分配并清除字节缓冲区(
mem = new Memory(size); mem.clear();
或 new byte[size]
),然后将特定字段写入您使用 [=17] 中的值确定的字节偏移量=].这确实使用了反射,但您可以为每个结构执行一次并存储一个名称映射以供以后使用。
- 对于从本机内存读取,使用平面缓冲区进行所有本机读取调用,以将本机开销减少到单个 read/write。您可以在读取该缓冲区时将该缓冲区转换为结构(对每个字段进行一次反射)或根据上述策略读取特定的字节偏移量。
我正在使用 JNA 包装本机代码(主要是 Fortran 77)。本机函数的输出(即结果)由一堆嵌套(自定义)types/structs 组成,我将其映射到 JNA 中相应的 Structure
。这些 Structures
主要由其他 Structures
的数组组成(因此结构 A 包含结构 B 的数组,结构 B 包含结构 C 的数组等)。
使用相同的基准测试(主要是通过记录时差)我发现大部分时间不是花在本机代码上,而是在 JNA 映射期间。 Fortran 子程序调用大约需要 50 毫秒,但总时间为 250 毫秒。
我发现
.setAutoWrite(false)
在我们的Structure
上减少了大约 2 倍的开销(总执行时间几乎减半)- 保持(静态分配的)数组尽可能小有助于保持较低的 JNA 开销
- 将
DOUBLE PRECISION
(double
)更改为REAL
(float
)似乎没有任何区别
在我们的案例中,是否还有其他技巧可以优化 JNA 性能?我知道我可以将我的结构扁平化为一维基元数组并使用直接映射,但我尽量避免这种情况(因为 encode/decode 这些结构会很痛苦)
如 JNA FAQ 中所述,直接映射将是您最好的性能提升,但您已将其排除在外。它还指出,每个本机调用的调用开销是另一个性能损失,您已通过更改 setAutoWrite()
.
您也确实提到了将您的结构扁平化为基元数组,但由于 encoding/decoding 复杂性而拒绝了。但是,朝着这个方向前进可能是下一个最佳选择,您当前面临的最大性能问题可能是 JNA Structure
使用反射访问和本机读取的组合。 Oracle notes:
Because reflection involves types that are dynamically resolved, certain Java virtual machine optimizations can not be performed. Consequently, reflective operations have slower performance than their non-reflective counterparts, and should be avoided in sections of code which are called frequently in performance-sensitive applications.
既然你在这里问一个与性能相关的问题并使用 JNA 结构,我只能假设你正在写一个 "performance-sensitive application"。在内部,结构是这样做的:
for (StructField structField : fields().values()) {
readField(structField);
}
它为每个字段执行一次本机读取,然后是这个,最终在引擎盖下使用反射。
setFieldValue(structField.field, result, true);
故事的寓意是,通常使用结构,通常每个字段都涉及本地读取+反射写入,或反射读取+本地写入。
您可以在不进行任何其他更改的情况下进行的第一步是 setAutoSynch(false)
结构。 (你已经用 "write" 版本完成了一半;它既读又写。)来自文档:
For extremely large or complex structures where you only need to access a small number of fields, you may see a significant performance benefit by avoiding automatic structure reads and writes. If auto-read and -write are disabled, it is up to you to ensure that the Java fields of interest are synched before and after native function calls via readField(String) and writeField(String,Object). This is typically most effective when a native call populates a large structure and you only need a few fields out of it. After the native call you can call readField(String) on only the fields of interest.
为了真正全力以赴,扁平化可能有助于消除任何反射开销。诀窍是使偏移转换变得容易。
一些方向,平衡复杂性与性能:
- 要写入本机内存,分配并清除字节缓冲区(
mem = new Memory(size); mem.clear();
或new byte[size]
),然后将特定字段写入您使用 [=17] 中的值确定的字节偏移量=].这确实使用了反射,但您可以为每个结构执行一次并存储一个名称映射以供以后使用。 - 对于从本机内存读取,使用平面缓冲区进行所有本机读取调用,以将本机开销减少到单个 read/write。您可以在读取该缓冲区时将该缓冲区转换为结构(对每个字段进行一次反射)或根据上述策略读取特定的字节偏移量。