为什么Java有IINC
字节码指令?
已有IADD
字节码指令可用于完成相同的操作。
为什么IINC
存在?
答案 0 :(得分:3)
作为already pointed out,单个iinc
指令比a iload
,sipush
,iadd
,istore
序列短。还有证据表明,执行常见的代码大小减少是一个重要的动机。
有处理前四个局部变量的专门指令,例如: aload_0
与aload 0
的作用相同,并且经常用于在操作数堆栈上加载this
引用。有ldc
指令可以引用前255个常量池项中的一个,而所有这些都可以由ldc_w
处理,分支指令使用两个字节进行偏移,因此只有过大的方法必须求助于goto_w
,以及iconst_n
-1
5
的说明,尽管这些都可以由bipush
处理,其中sipush
支持所有值都可以处理的值按ldc
,可以由iinc
取代。
所以非对称指令是常态。在典型的应用程序中,有许多小方法只有少数局部变量,较小的数字比较大的数字更常见。 i++
直接等效于独立的i+=smallConstantNumber
或#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
int hour;
int minute;
int seconds = -1;
boolean printed = false;
boolean secCounted = false;
unsigned long nextMillis = 1000;
void setup() {
lcd.begin(16, 2);
}
void loop() {
unsigned long currentMillis = millis();
if(currentMillis = nextMillis) _time(), nextMillis += 1000;
}
void _time(){
seconds += 1;
if(seconds == 60) seconds = 0, minute += 1;
if(minute == 60 && hour <= 12) minute = 0, hour += 1;
if(printed == false){
if(hour == 0) lcd.print("00");
if(hour != 0 && hour < 10) {lcd.print("0");
lcd.print(hour);}
if(hour > 9) lcd.print(hour);
lcd.print(":");
if(minute == 0) lcd.print("00");
if(minute != 0 && minute < 10) {lcd.print("0");
lcd.print(minute);}
if(minute > 9) lcd.print(minute);
lcd.print(":");
if(seconds == 0) lcd.print("00");
if(seconds != 0 && seconds < 10) {lcd.print("0");
lcd.print(seconds);}
if(seconds > 9) lcd.print(seconds);
printed = true;
}
}
表达式(应用于局部变量),这些表达式通常出现在循环中。通过在更简洁的代码中表达公共代码习惯用语而不失去表达所有代码的能力,您将大大节省整体代码大小。
正如前面已经指出的那样,在解释的执行中只有一点点机会可以更快地执行,这与编译/优化的代码执行无关。
答案 1 :(得分:2)
只有Java的原始设计者才能回答他们做出特定设计决策的原因。但是,我们可以推测:
IINC
不允许您执行ILOAD
/ SIPUSH
/ IADD
/ ISTORE
组合无法完成的任何操作。不同之处在于IINC
是单条指令,只需要3或6个字节,而4条指令序列显然更长。因此IINC
会略微减少使用它的字节码的大小。
除此之外,Java的早期版本使用了解释器,其中每个指令在执行期间都有开销。在这种情况下,使用单个IINC
指令可能比等效的替代字节码序列更快。请注意,JITting使这在很大程度上无关紧要,但IINC
可以追溯到Java的原始版本。
答案 2 :(得分:1)
看this table,有几个重要的区别。
iinc:通过有符号字节const
增加局部变量#index
iinc
使用register代替堆栈。iinc
只能通过签名的字节值递增。如果您想将[-128,127]
添加到整数,那么您可以使用iinc
,但只要您想要在该范围之外添加数字,就需要使用isub
,{{ 1}}或多个iadd
指令。E1:
我基本上是正确的,除了限制是有符号的短值(16位iinc
)。有一个[-32768,32767]
字节码指令可以修改wide
(以及其他一些指令)以使用16位数而不是8位数。
此外,请考虑将两个变量一起添加。如果其中一个变量不是常量,编译器将永远无法将其值内联到字节码,因此它不能使用iinc
;它必须使用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
字节码,允许最多16位的常量(wide
)。
如果我们尝试[-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
int i = 1;
byte c = 3;
i += c;