2009-06-03 9 views
18

問題:8000バイトを超えるJavaバイトコードにコンパイルする方法があります。 HotSpotには、8000バイトを超えるメソッドに対してJITを起動させないという魔法の限界があります。このメソッドはライブラリ内にあり、ライブラリのユーザがマジックリミットを無効にするためにHotSpotを設定する必要はありません。不要なgotosを取り除くJavaバイトコードオプティマイザはありますか?

観測:バイトコードを逆コンパイルすると、Eclipse Javaコンパイラが多くの無意味なものを生成することがわかります。 (javacはさらに悪いです)。つまりジャンプだけで到達可能なものがあります。明らかに、ジャンプ先にジャンプするジャンプは、ジャンプ先がジャンプ先にジャンプし、ジャンプ先を削除する必要があります。

質問:Java 5クラスファイルのためのバイトコードオプティマイザがありますが、無意味なジャンプチェーンを平らにし、不必要なgotosを削除しますか?

編集:

8698: goto 8548 
8701: goto 0 

明らかに、第二のgotoのみだけでなく秒で0

に直接ジャンプかもしれない8701へのジャンプで到達することができます。私のようなパターンを意味します調査は、この疑わしいパターンがより一般的です:

4257: if_icmpne 4263 
4260: goto 8704 
4263: aload_0 

明らかには、1が870にジャンプし、コンパイラは「等しくない」の比較に「等しい」の比較を逆にしたいと思います4とgotoを削除します。

+3

いくつかのアーキテクチャでは、(8ビットまたは16ビットのレジスタにアドレスを保持するため)相対分岐がどこまで行くことができるかに制限があります。そのため、相対分岐を使用して、プログラムカウンタサイズ。 JVMはそうですか? –

+0

* LABELS *はジャンプからのみ到達可能ですか? –

+0

JVMにヒントを与えるランタイムアノテーションは、この場合はすばらしいと思われます....しかし、私はそのようなものは存在しないと思っています(そして、素早くグーグルが何も出てこない)。 – Jared

答えて

0

8000バイトを超えるコンパイル方法?誰かがそのコードを理解していますか?テスト可能ですか?オプティマイザを悩ますのではなく、意味のある名前で複数の(プライベートな)メソッドに分割してみてください!

OK、場合によっては合法的な大きな方法があります。しかし、申し訳ありませんが、質問にはヒントはありません。

+1

彼はパーサを書いているように聞こえます。トークナイザループが大きくなるのは実際は非常に合理的です。 (私は人々に、そのような方法を壊すように強制しました。なぜなら、それは、n行を越えていて、彼らが従うのが難しいので、彼らが苦情を言いました...) –

+2

はい、私はコードを理解しています。また、再帰的降下法として、どのような再定式化よりも仕様に密接に関連しています。はい、それはユニットテストの大きなセットでテスト可能です。 – hsivonen

1

私はあなたの痛みを感じます。私はif(str.equals(...))コードの約5klocを持っていたパーサーを書く必要がありました。私はparse1、parse2などの行に沿っていくつかのメソッドに壊れました。もしparse1が解析された答えにならなかったら、parse2が呼び出されました。これは必ずしもベストプラクティスではありませんが、 。

+1

btw:あなたが知りたかったことがない場合は、parse1、parse2などのアプローチで「責任の連鎖」パターンを実装しました(この問題の悪い点や良い点ではありません。 ..) –

+0

私はループ内に1つの巨大なスイッチの代わりに複数のメソッドを持っていました。しかし、巨大なスイッチ構造は仕様に適合し、JITが起動するときにはより速くなります。 – hsivonen

+0

いつもより小さなスイッチ()を使用することができ、デフォルトのケースでは小さなスイッチで別のメソッドを呼び出します。スイッチのケースがカバーされています。これは、1つの大きなスイッチテーブルほど素晴らしいものではありませんが、うまくいくでしょう。私の場合は、文字列では機能しないのでスイッチを使用できませんでした。 – KitsuneYMG

0

デバッグシンボル(つまり、javacの-gフラグ)を指定してコンパイルしないと、違いがありますか?それは方法を魔法の限界以下に下げる可能性があります。

+0

少なくともEclipse Javaコンパイラでは、これはコンパイルされたメソッドのバイトコードの数には影響しません。 (私はデバッグテーブルが別々であると仮定します) – hsivonen

0

メソッドをサブメソッドにリファクタリングすることは不可能でしょうか?とにかく、現代のJITはそれらを呼び出す。

+2

以前はパーサーを手書きしていないと、スキャナ(lexer/tokenizer)が大きくなることがありますが、分割すると可読性が低下することがあります。もちろん私は彼のコードを見ていませんが、私はそこにいました... –

+0

私はすでにコントロールフローに影響を与えずに分割できる実際のトークナイザのアクションを分割しました。制御構造自体は巨大です。 – hsivonen

+1

@scott、私は前に手書きパーサを書いています。それが複雑すぎる場合(特定の理由で)、適切なパーサジェネレータを検討するのが合理的な時間になるかもしれません。 –

0

トークン化ループの場合は、データ駆動型のマッピングセットと適切なリフレクションを使用する方がよいでしょうか?

トークンの一致を、トークンの構文と関連する関数を実装するメソッドに関するデータにマップする構造体に格納します。ルックアップは構造全体にわたって最適化することができ、大きなループを避けることができます。

これは、データと実装を同期させるという問題を引き起こしますが、ドックレットまたは注釈を使用してコードベースからデータを生成することができます。

あなたの大きな方法が何であるかを正確に知らなければ、私たちはあなたが最良であると想定している方法で最適化しようとしています(とにかく不可能なようです)。

+0

いいえ、理想的には、データ構造の参照ではなく、コード内でジャンプするように状態遷移をコンパイルする必要があります。 – hsivonen

+0

確かに、マッチスペースの効率的な検索と結果のキャッシュによって、ルックアップを最適化できるかどうかによって異なります。 大きなハードコードされた一連のトークンマッチは、最適化にかなり抵抗するかもしれません(私はJITのヘルプを探していると思います)。 以前のプロジェクトでは、キャッシュされたメソッド参照を使ってかなり良い結果を得ました。 'ソース'の変更は、参照を無効にして新しくルックアップを宣言するだけでよく、通常の実行は高速で実行され、トークンマッチングによるパフォーマンス低下なしにメソッド呼び出しを起動できます。 – AndyT

0

あなたのクラスでバイトコードの縮小/難読化を実行するとパフォーマンスが向上しますか?たとえば、yguard、proguard、...

多分、あなたのユースケースが非常に特殊なので、asmを使ってクラスファイルポストプロセッサを書くことができます。

すべての無意味なgotosを削除しても、それは魔法の限界の下にあなたを持っていますか?

0

bytecode librariesのリストは、これまでに聞いたことがありますが、さまざまなことをしている他の多くの人たちと一緒に聞いていたのですが、BCELASMです。

関連する問題