17

ByteBuffer クラスを拡張するクラスを作成する方法はありますか?

ByteBuffer の一部の抽象メソッドはパッケージ プライベートであり、パッケージ java.nio を作成すると、セキュリティ例外がスローされます。

パフォーマンス上の理由からこれを行いたいと思います。たとえば、getInt には約 10 のメソッド呼び出しと、かなりの数の if があります。すべてのチェックが残され、メソッド呼び出しのみがインライン化され、ビッグ/スモール エンディアン チェックが削除された場合でも、私が作成したテストでは、約 4 倍高速になることが示されています。

4

7 に答える 7

15

ByteBuffer を拡張することはできません。神に感謝します。

保護された c-tors がない場合、b/c を拡張することはできません。なぜ神に感謝するのですか?実際のサブクラスが 2 つしかないため、JVM はByteBuffer を含むすべてのコードを大幅に最適化できます

最後に、実際にクラスを拡張する必要がある場合は、バイト コードを編集し、保護された属性 c-tor と public 属性を DirectByteBuffer (および DirectByteBufferR) に追加するだけです。とにかく基になる配列にアクセスできるため、HeapBufferを拡張しても何の目的もありません

そこに独自のクラスを使用-Xbootclasspath/pして追加し、必要なパッケージを拡張します(java.nioの外部)。それがその方法です。

もう 1 つの方法は、sun.misc.Unsafe を使用して、後でメモリに直接アクセスして必要なことを行うことですaddress()

パフォーマンス上の理由からこれを行いたいと思います。たとえば、getInt には約 10 回のメソッド呼び出しと、かなりの数の if があります。すべてのチェックが残され、メソッド呼び出しのみがインライン化され、ビッグ/スモール エンディアン チェックが削除された場合でも、私が作成したテストでは、約 4 倍高速になることが示されています。

gdb を使用して、実際に生成されたマシン コードをチェックすると、削除されるチェックの数に驚かれることでしょう。

なぜ人がクラスを拡張したいのか想像できません。オブジェクト指向ポリモーフの実行だけでなく、優れたパフォーマンスを可能にするために存在します。


編集:

クラスを宣言して Java ベリファイアをバイパスする方法

Unsafe の場合: Unsafe にはベリファイアをバイパスする 2 つのメソッドがあり、ByteBuffer を拡張するクラスがある場合は、それらのいずれかを呼び出すことができます。コンパイラのためだけに、パブリックアクセスと保護されたc-torを備えたByteBufferのハッキングされたバージョン(ただし、それは非常に簡単です)が必要です。方法は以下のとおりです。自己責任で使用できます。そのようにクラスを宣言した後、new キーワードを使用することもできます (適切な c-tor がある場合)。

public native Class defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);    
public native Class defineClass(String name, byte[] b, int off, int len);
于 2011-05-29T10:26:01.723 に答える
10

リフレクションを使用して保護レベルを無視することはできますが、それはパフォーマンスの目標を大きく損なうものです。

java.nio パッケージでクラスを作成することはできません。作成すること (および何らかの方法で結果を配布すること) は、Sun の Java ライセンスに違反し、理論的には法的な問題に巻き込まれる可能性があります。

ネイティブにならずにやりたいことを実行する方法はないと思いますが、時期尚早の最適化の誘惑に負けているのではないかと思います。テストが正しいと仮定すると (多くの場合、マイクロベンチマークはそうではありません)、実際のアプリケーションで ByteBuffer へのアクセスがパフォーマンスのボトルネックになると本当に確信していますか? アプリがそこに時間の 5% しか費やさず、フェッチしたデータの 95% を処理する場合に ByteBuffer.get() が 4 倍速くなるかどうかは、あまり関係ありません。

(おそらく純粋に理論上の) パフォーマンスのためにすべてのチェックをバイパスしたいというのは、良い考えとは言えません。パフォーマンス チューニングの基本ルールは、「まず正しく動作させ、次に速く動作させる」ことです。

編集:コメントに記載されているように、アプリが実際に ByteBuffer メソッドで 20 ~ 40% の時間を費やし、テストが正しい場合、それは 15 ~ 30% の高速化の可能性を意味します - 重要ですが、IMO は開始する価値はありませんJNIを使​​用するか、APIソースをいじります。最初に他のすべてのオプションを使い果たすようにします。

  • -server VM を使用していますか?
  • ByteBuffer の呼び出しを高速化するのではなく、呼び出し回数を減らすようにアプリを変更できますか?
  • プロファイラーを使用して、呼び出しがどこから来ているかを確認します - おそらくいくつかは完全に不要です
  • アルゴリズムを変更したり、ある種のキャッシングを使用したりすることができます
于 2009-03-08T23:04:06.540 に答える
2

ByteBuffer は抽象的なので、はい、それを拡張できます...しかし、実際にインスタンス化されたクラスを拡張したいのですが、おそらくできないと思います。インスタンス化された特定のメソッドがそのメソッドをオーバーライドして、ByteBuffer のメソッドよりも効率的である可能性もあります。

また、必要なすべてについて一般的に間違っている可能性が高いと思います-おそらくそれはあなたがテストしているものではありませんが、コードは何らかの理由でそこにある可能性があります(おそらく他のプラットフォーム上)。

自分が正しいと信じている場合は、バグを開き、彼らの意見を確認してください。

nio パッケージに追加したい場合は、Java を呼び出すときにブート クラスパスを設定してみてください。クラスを rt.jar の前に配置できるようにする必要があります。java -X と入力してその方法を確認します。-Xbootclasspath/p スイッチが必要です。

于 2009-03-08T23:03:06.777 に答える
1

Javaエージェントは、ByteBufferのバイトコードを変更し、コンストラクターのアクセス修飾子を変更する可能性があります。もちろん、JVMにエージェントをインストールする必要がありますが、それでもコンパイルしてサブクラスをコンパイルする必要があります。あなたがそのような最適化を考えているなら、あなたはそれに立ち向かわなければなりません!

私はそのような低レベルの操作を試みたことがありません。エージェントがそれにフックする前に、JVMがByteBufferを必要としないことを願っています。

于 2011-06-01T06:11:39.537 に答える
1

アクセス制限を回避する方法として +50 の報奨金 (リフレクションだけではできません。sun.misc.Unsafe などを使用する方法があるのではないでしょうか?)

答えは、Java のすべてのアクセス制限を回避する方法はありません。

  • sun.misc.Unsafeセキュリティ管理者の権限の下で動作するため、役に立たない
  • サーナムが言ったように:

ByteBuffer にはパッケージのプライベート抽象 _set および _get メソッドがあるため、オーバーライドできませんでした。また、すべてのコンストラクターはパッケージ プライベートであるため、呼び出すことはできません。

  • リフレクションを使用すると、多くのものをバイパスできますが、セキュリティ マネージャーが許可している場合に限ります。セキュリティマネージャを制御できない状況が多くあります。それはあなたに課せられています。コードがセキュリティ マネージャーのいじりに依存している場合、いわばすべての状況で「移植可能」または実行可能ではありません。

問題の結論は、バイト バッファをオーバーライドしようとしても問題が解決しないということです。

必要なメソッドを使用して、自分でクラスを実装する以外に選択肢はありません。メソッドを final にすることは、コンパイラが最適化を実行するのに役立ちます (ランタイム ポリモーフィズムとインライン化のためのコードを生成する必要性を減らします)。

于 2011-05-27T20:03:26.420 に答える
1

Unsafe インスタンスを取得する最も簡単な方法は、リフレクションを使用することです。ただし、リフレクションを使用できない場合は、別のインスタンスを作成できます。これは JNI を介して行うことができます。

バイトコードで、コンストラクターを呼び出さずにインスタンスを作成しようとしました。これにより、アクセス可能なコンストラクターを使用せずにオブジェクトのインスタンスを作成できます。ただし、バイトコードの VerifyError を取得したため、この ID は機能しません。オブジェクトにはコンストラクターが呼び出されている必要があります。


私がしていることは、直接 ByteBuffer をラップする ParseBuffer を用意することです。リフレクションを使用してUnsafe参照とaddress. バッファーの最後まで実行して JVM を強制終了することを避けるために、必要以上のページを割り当てますが、ページに触れない限り、物理メモリはアプリケーションに割り当てられません。これは、境界チェックがはるかに少なく、重要なポイントでのみチェックすることを意味します。

OpenJDK のデバッグ バージョンを使用すると、安全でない get/put メソッドが単一のマシン コード命令に変わることがわかります。ただし、これはすべての JVM で利用できるわけではなく、すべてのプラットフォームで同じ改善が得られるとは限りません。

このアプローチを使用すると、タイミングを約 40% 短縮できると言えますが、通常の Java コードにはないリスク、つまり JVM を強制終了する可能性があります。私が持っているユースケースは、単純な直接 ByteBuffer を使用する場合と比較して、Unsafe を使用して含まれるデータのオブジェクト作成を行わない XML パーサーおよびプロセッサです。私が XML パーサーで使用するトリックの 1 つは、getShort() および getInt() を使用して、各バイトを 1 つずつ調べるのではなく、一度に複数のバイトを調べることです。

Unsafe クラスへのリフレクションの使用は、一度発生するオーバーヘッドです。Unsafe インスタンスを取得すると、オーバーヘッドはありません。

于 2011-05-29T13:12:07.793 に答える
-1

私は、あなたが尋ねた質問ではなく、あなたが答えてほしい質問に答えています。あなたの本当の質問は、「どうすればこれをより速く進めることができますか?」ということです。答えは、「一度に整数を配列で処理し、単独では処理しない」です。

ボトルネックが本当に ByteBuffer.getInt() または ByteBuffer.getInt(location) である場合、クラスを拡張する必要はありません。既存の IntBuffer クラスを使用してデータを一括で取得し、より効率的な処理を行うことができます。

int totalLength = numberOfIntsInBuffer;
ByteBuffer myBuffer = whateverMyBufferIsCalled;
int[] block = new int[1024];
IntBuffer intBuff = myBuffer.asIntBuffer();
int partialLength = totalLength/1024;

//Handle big blocks of 1024 ints at a time
try{
  for (int i = 0; i < partialLength; i++) {
     intBuff.get(block);
     // Do processing on ints, w00t!
  }

  partialLength = totalLength % 1024; //modulo to get remainder
  if (partialLength > 0) {
    intBuff.get(block,0,partialLength);
    //Do final processing on ints
  }
} catch BufferUnderFlowException bufo {
   //well, dang!
}

これは、一度に int を取得するよりもはるかに高速です。設定された既知の適切な境界を持つ int[] 配列を反復処理すると、境界チェックと ByteBuffer がスローする可能性のある例外を排除することで、コードの JIT をより厳密にすることもできます。

さらにパフォーマンスが必要な場合は、コードを微調整するか、サイズを最適化した独自の byte[] から int[] への変換コードをロールすることができます。部分的なループ展開で IntBuffer メソッドの代わりにそれを使用して、パフォーマンスを改善することができました...しかし、それは決して提案されていません。

于 2011-06-01T17:01:05.177 に答える