2016-05-05 24 views
-1

JavaにIINCバイトコード命令があるのはなぜですか? 既にIADDバイトコード命令があります。 なぜIINCは存在するのですか?JavaにIINCバイトコード命令があるのはなぜですか?

+0

どうすればよいですか?あなたの反論は何ですか? – EJP

+0

@EJP最初の回答には私の反対が含まれています。 ILOADとIADDで同じことができるからです。なぜIINCを追加するのですか?私はそれが不必要だったように感じた。 – user2997204

答えて

2

だけのオリジナルデザイナーが答えることができます。しかし、私たちは推測することができます:

IINCは、ILOAD/SIPUSH/IADD/ISTOREコンボではまだ達成できないことはできません。違いは、IINCは単一の命令であり、3または6バイトしかかかりませんが、4命令の順序は明らかに長くなっています。したがって、IINCはそれを使用するバイトコードのサイズをわずかに縮小します。

これとは別に、Javaの初期のバージョンではインターフェラが使用されていました。インターペラでは、実行中にすべての命令にオーバーヘッドが発生します。この場合、単一のIINC命令を使用することは、同等の代替バイトコードシーケンスより高速である可能性がある。 JITtingはこれをほとんど無関係にしましたが、IINCはJavaの元のバージョンにさかのぼります。

1

this tableを見ると、いくつかの重要な違いがあります。

IINC:符号付きバイトCONST

  1. iincによってインクリメントローカル変数#INDEX代わりに、スタックのregisterを使用します。
  2. iincは、で符号化されたバイト値だけ増加します。 [-128,127]を整数に追加する場合は、iincを使用できますが、その範囲外の数値を追加するとすぐにisubiadd、または複数の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 -= 601iinc_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 
} 

はまた、我々はii += 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 
} 
+0

私はあなたの最初のポイントを得るが、私はあなたの第2ポイントが正しいとは思わない。 私はちょうどこれを見たので: "iinc 14 -601" – user2997204

+0

@ user2997204私の編集を参照 – Jeffrey

+0

@Jeffrey OracleのJVMはレジスタベースではなく、代わりにスタックに格納されています。また、あなたは多少それを暗示しましたが、フォーム間の劇的なサイズの違いを明示的に言及する価値があるかもしれません。 – Obicere

1

already pointed outとして単一iinc命令がiloadsipushiaddistoreシーケンスよりも短くなっています。一般的なコードサイズの削減を実行することが重要な動機であるという証拠もあります。

最初の4つのローカル変数を処理するための特別な手順があります。 aload_0aload 0と同じですが、オペランドスタックに参照をロードするために頻繁に使用されます。 ldc命令は最初の255個の定数プール項目の1つを参照することができるが、それらの全てはldc_wで扱うことができ、分岐命令はオフセットに2バイトを使用するので、過大な方法だけがgoto_wiconst_n命令〜5が存在するにもかかわらず、これらのすべてがbipushによって処理される可能性があり、すべてがによって処理される可能性のある値をサポートします。これは、ldcで置き換えられます。

したがって、非対称命令が標準です。典型的なアプリケーションでは、少数のローカル変数しか持たない小さなメソッドが多数あり、より小さい数値はより大きい数値よりも一般的です。 iincは、ループ内で頻繁に発生するスタンドアロンのi++またはi+=smallConstantNumber式(ローカル変数に適用される)と直接同等です。すべてのコードを表現する能力を失うことなく、よりコンパクトなコードで共通のコードイディオムを表現できることにより、全体のコードサイズを大幅に節約できます。

また、既に指摘したように、コンパイル/最適化されたコードの実行には関係のない、解釈された実行での実行速度がわずかです。