假设是,如果您使用的是 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 中的s 数组将在 Java 端unsigned char
公开为数组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 中的相应类型完全匹配的情况下使用。