2

N 個の要素を持つ C 文字列の配列があるとします。私の目標は、JNI を使用してその配列を Java 関数に渡し、同じ長さの新しい文字列配列を C 空間に戻すことです。現在、私は次のことを行っています。

  • NewObjectArray を使用して、長さ N の Java オブジェクト配列を生成します。
  • NewStringUTF/SetObjectArray を N 回呼び出して、個々の C 文字列を Java オブジェクト配列にボックス化します。
  • copyStrArr の呼び出し (以下のソース)。
  • (char *) の長さ N の配列を malloc で割り当てます。
  • GetObjectArrayElement/GetStringUTFChars を N 回呼び出して、返された Java オブジェクト配列から個々の Java String をアンボックスします。

参考までに、Java コードは次のようになります。

public static String[] copyStrArr(String []inArr)
{
    String []outArr = new String[inArr.length];
    for(int _i = 0; _i < outArr.length; _i++) {
        outArr[_i] = inArr[_i]; /* Normally real work would be done here */
    }
    return outArr;
}

「実際の」ケースでは、実際の作業は for ループ内で行われますが、ベンチマークではデータのコピーを作成するだけです。

N の値が大きい場合、これは遅くなります。めちゃくちゃ遅い。同様のサイズの int または double の配列を C から Java に移動し、その逆に移動すると、String[] の場合よりも ~70 倍速く実行されます。約 99.5% の時間は、データのボックス化とボックス化解除に費やされます。プリミティブの場合、JNI は {Set,Get}ArrayRegion 関数を提供して、プリミティブ配列を C 空間から Java 空間に一括コピーします。これははるかに高速です。

byte[] を仲介として使用してデータを Java 空間に取得し、Java で個々の String Object boxing を実行することが提案されています (JVM が物事を最適化できる場所)。ベンチマークによると、これは元のテストよりもパフォーマンスがわずかに悪く、オーバーヘッドの多くが Java に移動しています。これの一部は、Java で byte[] を最適にアンボックス化/ボックス化していない可能性があることです。私は次のことをしています:

  • NewByteArray で十分な大きさの byte[] を割り当てる
  • SetByteArrayRegion を N 回呼び出して、バイト [] を設定する
  • copyBytArray の呼び出し (以下のソース)
  • GetByteArrayRegion を呼び出し、結果全体を C 空間にコピーして戻す
  • (char *) の十分に大きな配列を割り当てる
  • 結果から N 文字列のそれぞれを新しく割り当てられた配列にコピーします。

私のJavaコードは次のようになります。

public static byte[] copyBytArr(byte []inArr)
{
    String[] tokInArr = new String(inArr, UTF8_CHARSET).split("\0");
    String []tokOutArr = new String[tokInArr.length];
    int len = 0;
    for(int _i = 0; _i < tokOutArr.length; _i++) {
        tokOutArr[_i] = tokInArr[_i]; /* Normally real work would be done here */
        len += (tokInArr[_i].length() + 1);
    }
    byte[] outArr = new byte[len];
    int _j = 0;
    for(int _i = 0; _i < tokOutArr.length; _i++) {
        byte[] bytes = tokOutArr[_i].getBytes(UTF8_CHARSET);
        for(int _k = 0; _k < bytes.length; _k++) {
            outArr[_j++] = bytes[_k];
        }
        outArr[_j++] = '\0';
    }
    return outArr;
}

このテストでは、オーバーヘッドの約 55% が Java で費やされ、残りはボックス化/ボックス化解除に費やされました。

Java は UTF-16 を使用しているため、私のオーバーヘッドの一部は C で UTF-8 データを使用しているという事実に関連していることが示唆されています。これは避けられません。

これをより効率的に行う方法について誰かアイデアがありますか?

4

1 に答える 1

1

あなたの問題は、多くの文字列オブジェクトの割り当てだと思います。実際のパフォーマンスを得るには、大きな byte[] を交換し、文字列処理のためにバイト配列を「指す」ラッパー クラスを使用する必要があります。C の chars[] から文字列オブジェクトを作成している限り、実際のスループットは得られません。

FST は、「実際の」オブジェクトを作成する必要なく byte[] データを操作するために、「StructString」クラスで同様のことを行っています。

データ交換をさらに高速化するには、メモリ マップ ファイルを使用して共有メモリを作成し、Unsafe または ByteBuffers を介してこれにアクセスすることをお勧めします。

于 2013-10-22T16:49:22.807 に答える