JavaにIINCバイトコード命令があるのはなぜですか? 既にIADDバイトコード命令があります。 なぜIINCは存在するのですか?JavaにIINCバイトコード命令があるのはなぜですか?
答えて
だけのオリジナルデザイナーが答えることができます。しかし、私たちは推測することができます:
IINCは、ILOAD/SIPUSH/IADD/ISTOREコンボではまだ達成できないことはできません。違いは、IINCは単一の命令であり、3または6バイトしかかかりませんが、4命令の順序は明らかに長くなっています。したがって、IINCはそれを使用するバイトコードのサイズをわずかに縮小します。
これとは別に、Javaの初期のバージョンではインターフェラが使用されていました。インターペラでは、実行中にすべての命令にオーバーヘッドが発生します。この場合、単一のIINC命令を使用することは、同等の代替バイトコードシーケンスより高速である可能性がある。 JITtingはこれをほとんど無関係にしましたが、IINCはJavaの元のバージョンにさかのぼります。
this tableを見ると、いくつかの重要な違いがあります。
IINC:符号付きバイトCONST
iinc
によってインクリメントローカル変数#INDEX代わりに、スタックのregisterを使用します。iinc
は、で符号化されたバイト値だけ増加します。[-128,127]
を整数に追加する場合は、iinc
を使用できますが、その範囲外の数値を追加するとすぐにisub
、iadd
、または複数のiinc
命令を使用する必要があります。
E1:
TL; DR
私は限界が短い値(16ビット[-32768,32767]
)署名されることを除いて、基本的に正しかったです。 wide
バイトコード命令があり、iinc
(およびその他の命令)を変更して、8ビットの代わりに16ビットの数値を使用するようにしました。
さらに、2つの変数を一緒に追加することを検討してください。変数の1つが定数でない場合、コンパイラはその値をバイトコードにインライン化できないため、iinc
を使用することはできません。 iadd
を使用する必要があります。
package SO37056714;
public class IntegerIncrementTest {
public static void main(String[] args) {
int i = 1;
i += 5;
}
}
私は上記のコード断片で実験するつもりです。それは、期待どおり使用iinc
です。
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iinc 1, 5
5: return
}
i += 127
予想通りiinc
使用しています。
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iinc 1, 127
5: return
}
i += 128
もうiinc
を使用しますが、代わりにiinc_w
しません:
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iinc_w 1, 128
8: return
}
i -= 601
もiinc_w
を使用しています。
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iinc_w 1, -601
8: return
}
_w
サフィックスは定数を可能wide
バイトコードを指し、最大16ビット([-32768, 32767]
)。我々はi += 32768
をしようとした場合
、私たちは、私が上記の予測何が表示されます:
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iload_1
3: ldc #16 // int 32768
5: iadd
6: istore_1
7: return
}
はまた、我々はi
(i += c
)に別の変数を追加している場合を考えます。コンパイラはc
が定数かどうかわからないので、c
のバイトコードの値をインライン化できません。それはあまりにもこのような場合のためにiadd
使用します。彼らは、特定の設計上の決定をした理由のJavaの
int i = 1;
byte c = 3;
i += c;
$ javap -c IntegerIncrementTest.class
Compiled from "IntegerIncrementTest.java"
public class SO37056714.IntegerIncrementTest {
public SO37056714.IntegerIncrementTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iconst_3
3: istore_2
4: iload_1
5: iload_2
6: iadd
7: istore_1
8: return
}
私はあなたの最初のポイントを得るが、私はあなたの第2ポイントが正しいとは思わない。 私はちょうどこれを見たので: "iinc 14 -601" – user2997204
@ user2997204私の編集を参照 – Jeffrey
@Jeffrey OracleのJVMはレジスタベースではなく、代わりにスタックに格納されています。また、あなたは多少それを暗示しましたが、フォーム間の劇的なサイズの違いを明示的に言及する価値があるかもしれません。 – Obicere
はalready pointed outとして単一iinc
命令がiload
、sipush
、iadd
、istore
シーケンスよりも短くなっています。一般的なコードサイズの削減を実行することが重要な動機であるという証拠もあります。
最初の4つのローカル変数を処理するための特別な手順があります。 aload_0
はaload 0
と同じですが、オペランドスタックに参照をロードするために頻繁に使用されます。 ldc
命令は最初の255個の定数プール項目の1つを参照することができるが、それらの全てはldc_w
で扱うことができ、分岐命令はオフセットに2バイトを使用するので、過大な方法だけがgoto_w
とiconst_n
命令〜5
が存在するにもかかわらず、これらのすべてがbipush
によって処理される可能性があり、すべてがによって処理される可能性のある値をサポートします。これは、ldc
で置き換えられます。
したがって、非対称命令が標準です。典型的なアプリケーションでは、少数のローカル変数しか持たない小さなメソッドが多数あり、より小さい数値はより大きい数値よりも一般的です。 iinc
は、ループ内で頻繁に発生するスタンドアロンのi++
またはi+=smallConstantNumber
式(ローカル変数に適用される)と直接同等です。すべてのコードを表現する能力を失うことなく、よりコンパクトなコードで共通のコードイディオムを表現できることにより、全体のコードサイズを大幅に節約できます。
また、既に指摘したように、コンパイル/最適化されたコードの実行には関係のない、解釈された実行での実行速度がわずかです。
どうすればよいですか?あなたの反論は何ですか? – EJP
@EJP最初の回答には私の反対が含まれています。 ILOADとIADDで同じことができるからです。なぜIINCを追加するのですか?私はそれが不必要だったように感じた。 – user2997204