为什么Java有IINC字节码指令?

问题描述 投票:0回答:3

为什么Java有IINC字节码指令?已经有一个IADD字节码指令可用于完成相同的操作。

那么为什么IINC存在?

java bytecode
3个回答
2
投票

只有Java的原始设计者才能回答他们做出特定设计决策的原因。但是,我们可以推测:

IINC不允许你做一些ILOAD / SIPUSH / IADD / ISTORE组合无法完成的任何事情。不同之处在于IINC是单指令,只需要3或6个字节,而4指令序列显然更长。所以IINC略微减少了使用它的字节码的大小。

除此之外,早期版本的Java使用了解释器,其中每个指令在执行期间都有开销。在这种情况下,使用单个IINC指令可能比等效的替代字节码序列更快。请注意,JITting使这在很大程度上无关紧要,但IINC可以追溯到Java的原始版本。


3
投票

作为already pointed out,单个iinc指令比iloadsipushiaddistore序列短。还有证据表明,执行常见的代码大小减少是一个重要的动机。

有处理前四个局部变量的专门指令,例如: aload_0aload 0的作用相同,它经常用于在操作数堆栈上加载this引用。有一个ldc指令能够引用前255个常量池项中的一个,而所有这些都可以由ldc_w处理,分支指令使用两个字节进行偏移,因此只有过大的方法必须求助于goto_wiconst_n指令-15存在,尽管这些都可以由bipush处理,sipush支持所有也可以全部由ldc处理的值,iinc可以被i++取代。

因此,不对称指令是常态。在典型的应用程序中,有许多小方法只有少数局部变量,较小的数字比较大的数字更常见。 i+=smallConstantNumber直接等同于独立的this tableiinc表达式(应用于局部变量),它们通常出现在循环中。通过在更简洁的代码中表达公共代码习惯用语而不失去表达所有代码的能力,您将大大节省整体代码大小。

正如已经指出的那样,在解释的执行中只有很少的机会可以更快地执行,这与编译/优化的代码执行无关。


1
投票

看看register,有几个重要的区别。

iinc:通过带符号的字节const增加局部变量#index

  1. iinc使用[-128,127]而不是堆栈。
  2. iinc只能通过有符号字节值递增。如果你想将isub添加到一个整数,那么你可以使用iadd,但是只要你想要在该范围之外添加一个数字,就需要使用iinc[-32768,32767]或多个wide指令。

Q1:

TL;DR

我基本上是正确的,除了限制是有符号的短值(16位iinc)。有一个iinc字节码指令修改iadd(和其他一些指令)使用16位数而不是8位数。

另外,考虑将两个变量一起添加。如果其中一个变量不是常量,编译器将永远无法将其值内联到字节码,因此它不能使用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

[-32768, 32767]后缀是指i += 32768字节码,它允许常数高达16位($ 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

另外,考虑我们将另一个变量添加到cc)的情况。编译器不知道iadd是否是常量,因此它不能将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 }

qazxswpoi
qazxswpoi
© www.soinside.com 2019 - 2024. All rights reserved.