java - 当您需要/将数据作为 native java 数组时,carrays.i 与 arrays_java.i 的 SWIG 效率

标签 java swig

我很好奇它在痛饮中说了什么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/

相关文章:

java - Mocking Guice 提供的对象

使用带有 Junit 的 Spring Boot 的 Wiremock 进行 Java 集成测试时出现 java.net.SocketTimeoutException

python - libccv - 如何从内存中的字节读取图像

c++ - 将 C++ 类实例暴露给 python 嵌入式解释器

java - Sedgewick Quicksort 没有完全意义

java - 将服务帐户(json 文件)位置传递给 application.properties

Java 泛型 + 静态工厂方法 = [ panic ]

swig - 使用 Swig 忽略特定的重载方法

java - 如何从 Grails 应用程序使用 JNI native 库

c# - 使用 SWIG 从 C++ 初始化对 C# 共享指针的引用