问题
Why does Java have an IINC
bytecode instruction?
There is already an IADD
bytecode instruction that can be used to accomplish the same.
So why does IINC
exist?
回答1:
Only the original designers of Java can answer why they made particular design decisions. However, we can speculate:
IINC
does not let you do anything that can't already be accomplished by a ILOAD
/SIPUSH
/IADD
/ISTORE
combo. The difference is that IINC
is a single instruction, which only takes 3 or 6 bytes, while the 4 instruction sequence is obviously longer. So IINC
slightly reduces the size of bytecode that uses it.
Apart from that, early versions of Java used an interpreter, where every instruction has overhead during execution. In this case, using a single IINC
instruction could be faster than the equivalent alternative bytecode sequence. Note that JITting has made this largely irrelevant, but IINC
dates back to the original version of Java.
回答2:
As already pointed out a single iinc
instruction is shorter than the a iload
, sipush
, iadd
, istore
sequence. There is also evidence, that performing a common-case code size reduction was an important motivation.
There are specialized instructions for dealing with the first four local variables, e.g. aload_0
does the same as aload 0
and it will be used often for loading the this
reference on the operand stack. There’s an ldc
instruction being able to refer to one of the first 255 constant pool items whereas all of them could be handled by ldc_w
, branch instructions use two bytes for offsets, so only overly large methods have to resort to goto_w
, and iconst_n
instructions for -1
to 5
exist despite these all could be handled by bipush
which supports values which all also could all be handled by sipush
, which could be superseded by ldc
.
So asymmetric instructions are the norm. In typical applications, there are a lot of small methods with only a few local variables and smaller numbers are more common than larger numbers. iinc
is a direct equivalent to stand-alone i++
or i+=smallConstantNumber
expressions (applied to local variables) which often occur within loops. By being able to express common code idioms in more compact code without loosing the ability to express all code, you’ll get great savings in overall code size.
As also already pointed out, there is only a slight opportunity for faster execution in interpreted executions which is irrelevant for compiled/optimized code execution.
回答3:
Looking at this table, there are a couple important differences.
iinc: increment local variable #index by signed byte const
iinc
uses a register instead of the stack.iinc
can only increment by a signed byte value. If you want to add[-128,127]
to an integer, then you could useiinc
, but as soon as you want to add a number outside that range you would need to useisub
,iadd
, or multipleiinc
instructions.
E1:
TL;DR
I was basically right, except that the limit is signed short values (16 bits [-32768,32767]
). There's a wide
bytecode instruction which modifies iinc
(and a couple other instructions) to use 16 bit numbers instead of 8 bit numbers.
Additionally, consider adding two variables together. If one of the variables is not constant, the compiler will never be able to inline its value to bytecode, so it cannot use iinc
; it will have to use iadd
.
package SO37056714;
public class IntegerIncrementTest {
public static void main(String[] args) {
int i = 1;
i += 5;
}
}
I'm going to be experimenting with the above piece of code. As it is, is uses iinc
, as expected.
$ 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
uses iinc
as expected.
$ 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
does not use iinc
anymore, but instead 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
also uses 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
}
The _w
suffix refers to the wide
bytecode, which allows for constants up to 16 bits ([-32768, 32767]
).
If we try i += 32768
, we will see what I predicted above:
$ 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
}
Additionally, consider the case where we are adding another variable to i
(i += c
). The compiler doesn't know if c
is constant or not, so it cannot inline c
's value to bytecode. It will use iadd
for this case too:
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
}
来源:https://stackoverflow.com/questions/37056714/why-does-java-have-an-iinc-bytecode-instruction