2

swig/java で配列を処理する 2 つの基本的な方法の効率に関して、swig のドキュメントに記載されている内容に興味があります。特に、とにかくネイティブ Java 配列との間でコピーする必要がある場合、carrays.i の方法が本当に効率的かどうか疑問に思っていますか? たとえば、呼び出す C func void populate(int x[]) があり、その結果をネイティブ Java int[] を受け取る Java func に渡す必要があるとします。それをcarraysの方法で行うには、次のことが必要です。

%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);
} 

次に、ネイティブの int[] を取るネイティブの Java 関数を呼び出します

f(nativeArray);

それは本当により効率的ですか

%include "arrays_java.i"
int[] nativeArray = new int[10000000];
populate(nativeArray);
f(nativeArray);

前者の場合、とにかくコピーをしなければならないので?

4

1 に答える 1

3

carrays.i を使用している場合、Java 配列間でコピーを行ったり来たりしていないという前提があります。その代わりに、どこでも carrays.i タイプを使用する必要があります。

測定できるオーバーヘッドにはまだ 2 つのタイプがあります。JNI 呼び出しは特定のペナルティを被り、別のペナルティをコピーします。carray.i を使用すると、正確に識別したように、JNI 呼び出しの価格が低く抑えられます。Get<PrimitiveType>ArrayElementsさらに、JVM によっては、への呼び出しによってコピーが導入される場合もあります。

しかし驚くべきことは、arrays_java.i の実装が他の方法で予想されるよりも多くのコピーを作成することです。たとえば、typemap で使用される 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 で最も近い型は ですがbytebyte署名されているため、値の範囲を表すためにunsigned char、C の の配列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 配列へのポインターを渡します (まだコピーである場合とそうでない場合があります。オプションの 3 番目の引数で確認できます。GetIntArrayElementsこれは、JVM がプロセスでコピーを作成したかどうかを示すブール値です)。

これらの型マップは、Java 配列を「そのまま」渡し、JVM からポインタを取得して C 関数内で使用します。関数が成功ReleaseIntArrayElementsすると、3 番目のパラメータが 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 の対応する型と完全に一致する場合にのみ使用できます。

于 2012-10-07T10:27:51.057 に答える