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 データを使用しているという事実に関連していることが示唆されています。これは避けられません。
これをより効率的に行う方法について誰かアイデアがありますか?