5

String -> byte[] 変換を集中的に使用するコードを継承し、その逆も同様です。基本的に、Java オブジェクトは、その構成部分を文字列に変換する方法を知っており、文字列はバイト [] に変換されます。次に、このバイト配列は、JNI を介して C++ コードに渡されます。C++ コードは、byte[] を C++ std::strings に再構成し、それらを使用して、Java オブジェクトをミラーリングする C++ オブジェクトをブートストラップします。もう少し続きがありますが、これはこのコードがどのように機能するかの概要です。通信は双方向でこのように機能するため、C++ -> Java 遷移は、前述の Java -> C++ 遷移のミラー イメージです。

このコードの一部 (String から byte[] への実際の変換) が、予期せずプロファイラーに表示され、大量の CPU を消費しています。確かに、大量のデータが転送されていますが、これは予想外のボトルネックです。

コードの基本的な概要は次のとおりです。

public void convertToByteArray(String convert_me, ByteArrayOutputStream stream)
{
  stream.write(convert_me.getBytes());
}

機能にはもう少しありますが、それほど多くはありません。上記の関数は、文字列/文字列化されたオブジェクトごとに 1 回呼び出され、すべての構成要素が ByteArrayOutputStream に書き込まれた後、ByteArrayOutputStream は byte[] に変換されます。呼び出しを抽出して上記をよりプロファイラーに適したバージョンに分解するとconvert_me.getBytes()、この関数の時間の 90% 以上が getBytes() 呼び出しに費やされていることがわかります。

getBytes() 呼び出しのパフォーマンスを改善する方法はありますか、または同じ変換を達成するための別の潜在的に高速な方法はありますか?

変換されるオブジェクトの数は非常に多くなります。本番データの小さなサブセットのみを使用しているプロファイリングの実行では、1,000 万以上の上記の変換関数の呼び出しが見られます。

プロジェクトの本番環境へのリリースが近づいているため、現時点では実行できないいくつかの回避策があります。

  • シリアライゼーション インターフェイスを書き直して、JNI レイヤー全体で String オブジェクトを渡すだけにします。これは (私にとって) 状況を改善する明白な方法ですが、シリアライゼーション レイヤーの大幅な再設計が必要になります。今週初めに UAT に移行するという事実を考えると、この種の複雑な変更を行うには遅すぎます。これは次のリリースに向けた私の最優先事項なので、完了します。ただし、それまでは回避策が必要ですが、これまでのところコードは機能しており、何年も使用されており、ほとんどの問題は解決しています。まぁ、演出は別として。
  • JVM (現在は 1.5) を変更することもできません。残念ながら、これはクライアントのマシンにインストールされているデフォルトの JVM であり、1.6 への更新 (この場合は高速である場合とそうでない場合があります) は残念ながら不可能です。大規模な組織で働いたことのある人なら、おそらくその理由を理解しているでしょう...
  • これに加えて、すでにメモリの制約に直面しているため、少なくともより大きな文字列とそのバイト配列表現をキャッシュしようとすると、洗練されたソリューションになる可能性がありますが、解決するよりも多くの問題が発生する可能性があります。
4

4 に答える 4

4

問題の一部は、Java String が UTF-16 形式 (つまり、1 文字あたり 2 バイト) であることである可能性があると推測しています。そのためgetBytes()、現在の文字セットに応じて、各 UTF-16 要素を 1 バイトまたは 2 バイトに変換する一連の作業を行っています。

CharsetEncoderを使用してみましたか。これにより、文字列エンコーディングをより詳細に制御できるようになり、デフォルトのgetBytes実装でオーバーヘッドの一部をスキップできるようになります。

または、文字セットを明示的に に指定して、文字セットとしてUS-ASCIIgetBytesを使用しようとしましたか?

于 2009-06-21T11:36:00.927 に答える
2

いくつかのオプションが表示されます。

  • Latin-1 文字列がある場合は、文字列内の文字の上位バイトを分割するだけで済みます (Charset もこれを行うと思います)。
  • 複数のコアがある場合は、作業を複数のコアに分割することもできます (fork-join フレームワークには 1.5 へのバックポートが 1 回ありました)。
  • また、データを stringbuilder に構築し、最後に一度だけバイト配列に変換することもできます。
  • GC/メモリの使用量を見てください。メモリの使用率が高すぎると、頻繁に GC が中断されるため、アルゴリズムが遅くなる可能性があります
  • 于 2009-06-21T19:19:30.550 に答える
    1

    常に変換するのと同じ文字列である場合は、結果を WeakHashMap にキャッシュできます。

    また、getBytes() メソッド (SDK をインストールするとソースが利用可能になります) を調べて、その正確な動作を確認してください。

    于 2009-06-21T14:35:39.457 に答える