我很好奇它在痛饮中说了什么docs关于 swig/java 中处理数组的两种基本方法的效率。特别是,我想知道如果您最终需要复制到 native java 数组或从 native java 数组复制,那么 crays.i 方法是否真的更有效?例如。假设我有一个 C func void populate(int x[]) 要调用,然后我需要将结果传递给一个采用 native java int[] 的 Java func。要以 crays 的方式做到这一点,我需要:
%include "carrays.i"
%array_class(int, intArray);
intArray array = new intArray(10000000);
populate(array);
然后复制到 native java数组:
int[] nativeArray = new int[10000000];
for(int i = 0; i < 10000000; ++i)
{
nativeArray[i] = array.getitem(i);
}
然后调用我的 native java 函数,该函数采用 native int[]
f(nativeArray);
这真的比
更有效率吗?%include "arrays_java.i"
int[] nativeArray = new int[10000000];
populate(nativeArray);
f(nativeArray);
既然在前一种情况下,你无论如何都必须进行复制?
最佳答案
假设如果您使用 crays.i,那么您也不会在 Java 数组之间来回复制 - 您会希望在任何地方都使用 crays.i 类型,而不是那样。
您仍然可以预期测量两种类型的开销 - JNI 调用会产生某种惩罚并复制另一种惩罚。使用 cray.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
如此签名是为了表示 unsigned char
数组的值范围C 中的 s 将作为 short
的数组公开在Java方面。自 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
的可选第三个参数进行检查,它是一个 boolean 值,指示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 中的相应类型完全匹配的情况下使用。
关于java - 当您需要/将数据作为 native java 数组时,carrays.i 与 arrays_java.i 的 SWIG 效率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12694238/