我很好奇它在swig docs中所说的关于swig / java中处理数组的两种基本方法的效率。特别是,我想知道如果你最终需要复制到本机java数组/从本机java数组中复制carrays.i的方式是否真的更有效?例如。说我有一个C函数void populate(int x [])来调用然后我需要将结果传递给一个带有本机java int []的Java函数。为了做到这一点,我需要:
%include "carrays.i"
%array_class(int, intArray);
intArray array = new intArray(10000000);
populate(array);
然后复制到本机java数组:
int[] nativeArray = new int[10000000];
for(int i = 0; i < 10000000; ++i)
{
nativeArray[i] = array.getitem(i);
}
然后调用我的原生java函数,它接受一个原生的int []
f(nativeArray);
这是否真的比
更有效率%include "arrays_java.i"
int[] nativeArray = new int[10000000];
populate(nativeArray);
f(nativeArray);
因为在前一种情况下,你还是要做副本吗?
答案 0 :(得分:3)
假设如果你正在使用carrays.i那么你也不是在Java数组之间来回复制 - 你想在任何地方使用carrays.i类型而不是那个。
仍有两种类型的开销可以衡量 - JNI调用会产生一定的惩罚并复制另一个惩罚。使用carray.i,后者保持低价,因为您正确识别了更多JNI呼叫的价格。另外,对Get<PrimitiveType>ArrayElements
的调用也可能会引入一个副本,具体取决于JVM。
然而,令人惊讶的是,arrays_java.i的实现会产生比您预期更多的副本,例如,类型映射使用的SWIG_JavaArrayIn函数包含以下内容:
for (i=0; i<sz; i++) JAVA_TYPEMAP_ARRAY_ELEMENT_ASSIGN(CTYPE)
并且在输出类型映射中也发生了类似的相应副本(通过赋值):
for (i=0; i<sz; i++) arr[i] = (JNITYPE)result[i];
(这是JVM可能复制的补充!)
然而,所有这些的推理都是合理的 - 这些类型图必须支持“奇怪”的情况,其中使用的JNITYPE并不完全映射到C类型。可能出现这种情况的一个示例是unsigned char
数组 - Java中最接近的类型是byte
,但是byte
是有符号的,以便表示数组的值范围C中的unsigned char
s将在Java端显示为short
的数组。由于sizeof(jshort) != sizeof(unsigned char)
内存布局不兼容,因此需要复制并填写并返回。
如果这困扰你(我强烈建议使用基准测试),那么仍然可以编写自己的高效字体图,它们使用您希望的JNI调用,例如:
%module test
%typemap(jtype) int arr[ANY] "int[]"
%typemap(jstype) int arr[ANY] "int[]"
%typemap(jni) int arr[ANY] "jintArray"
%typemap(javain) int arr[ANY] "$javainput"
%typemap(in) int arr[ANY] {
// check the size is compatible here also
$1 = JCALL2(GetIntArrayElements, jenv, $input, 0);
}
%typemap(freearg) int arr[ANY] {
if ($1) {
JCALL3(ReleaseIntArrayElements, jenv, $input, $1, JNI_ABORT);
}
}
%typemap(argout) int arr[ANY] {
JCALL3(ReleaseIntArrayElements, jenv, $input, $1, 0);
$1 = NULL;
}
%inline %{
void populate(int arr[100000]) {
for (unsigned i = 0; i < 100000; ++i) {
arr[i] = -i;
}
}
%}
将指针传递给从VM获取的Java数组(可能是或者可能不是副本,您可以使用可选的第三个参数检查GetIntArrayElements
这是一个布尔值,指示JVM是否创建了是否在过程中复制。)
这些类型映射以“原样”方式传递Java数组,然后从JVM获取指针以在C函数中使用。如果函数成功,则调用ReleaseIntArrayElements
,第三个参数为0 - 这确保了任何修改在Java中也是可见的。如果调用不成功,那么将使用JNI_ABORT
调用该函数,如果返回的指针是副本,则不会显示任何更改。
我们可以称之为:
public class run {
public static void main(String[] argv) {
System.loadLibrary("test");
int[] arr = new int[100000];
test.populate(arr);
// Only print 40 to avoid spamming my screen!
for (int i = 0; i < 40; ++i) {
System.out.println(arr[i]);
}
}
}
可能有0个副本,但只能在Java中的类型与C中相应类型完全匹配的情况下使用。