【问题标题】:Static functions inlining in JavaJava中的静态函数内联
【发布时间】:2012-06-22 17:43:00
【问题描述】:

第一个代码

public static int pitagoras(int a, int b)
{
    return (int) Math.sqrt(a*a + b*b);
}

public static int distance(int x, int y, int x2, int y2)
{
    return pitagoras(x - x2, y - y2);
}

distance 经常被调用。当我用javac 编译它然后用javap -c 反编译时,我得到了这个字节码:

public static int pitagoras(int, int);
  Code:
   0:   iload_0
   1:   iload_0
   2:   imul
   3:   iload_1
   4:   iload_1
   5:   imul
   6:   iadd
   7:   i2d
   8:   invokestatic    #24; //Method java/lang/Math.sqrt:(D)D
   11:  d2i
   12:  ireturn

public static int distance(int, int, int, int);
  Code:
   0:   iload_0
   1:   iload_2
   2:   isub
   3:   iload_1
   4:   iload_3
   5:   isub
   6:   invokestatic    #34; //Method pitagoras:(II)I
   9:   ireturn

看来javac没有优化第二个功能distance

第二个代码,我认为更快:

public static int distance(int x, int y, int x2, int y2)
{
    return (int) Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
}

及其字节码:

public static int distance(int, int, int, int);
  Code:
   0:   iload_0
   1:   iload_2
   2:   isub
   3:   iload_0
   4:   iload_2
   5:   isub
   6:   imul
   7:   iload_1
   8:   iload_3
   9:   isub
   10:  iload_1
   11:  iload_3
   12:  isub
   13:  imul
   14:  iadd
   15:  i2d
   16:  invokestatic    #24; //Method java/lang/Math.sqrt:(D)D
   19:  d2i
   20:  ireturn

invokestatic 快到和内联静态函数一样了吗?为什么javac 没有优化这个?或者它实际上是经过优化的,这两个代码会给出相同的结果,但我错过了什么?

【问题讨论】:

  • 离题:这个人叫Pythagoras 而不是Pitagoras...
  • 也许它会在运行时由 JIT 完成...
  • @brimborium 它看起来在西班牙语中是用“i”而不是“y”写成的:es.wikipedia.org/wiki/Pit%C3%A1goras
  • @GuillaumePolet 这很有趣(也很烦人)。毕达哥拉斯是一个希腊名字,所以我认为它应该拼写为希腊。

标签: java optimization inlining


【解决方案1】:

javac 没有优化。这就是 JVM 实现(通常是 HotSpot)的工作。

曾经在javac 中进行过一些优化,但它们使代码复杂化,并且据称倾向于安排代码以抑制 HotSpot 优化。

HotSpot 优化通常在数千次迭代后动态完成(可配置,默认取决于使用“客户端”、“服务器”还是分层版本)。

语言规范要求javac 做一些事情,例如内联常量和组合文字字符串。

【讨论】:

    【解决方案2】:

    Java 语言没有定义内联函数。许多(也许是大多数)即时 (JIT) 编译器会动态地(在运行时)将此类静态函数调用替换为内联代码。

    【讨论】:

      【解决方案3】:

      我相信两个版本的性能会相似,因为 JVM 使用 JIT 来提高性能。

      【讨论】:

        【解决方案4】:

        您正在寻找的优化类型(内联)不一定发生在编译时,但很有可能即时 (JIT) 编译器会在运行时执行它。

        因此,您不太可能看到内联发生在字节码级别,更有可能的是,它会在程序执行期间发生在本机代码级别。

        【讨论】:

          【解决方案5】:

          给出的答案是正确的:javac 不会内联方法,因为它可能不是最好的做法。

          假设distance() 方法偶尔被调用一次,但并不经常调用。通过内联 pitagoras() 来优化它会减慢几乎不使用的东西的编译速度。

          另一方面,Hotspot 知道一个方法何时被调用以及被调用了多少次。如果该方法经常执行,那么 Hotspot 可能会内联它并将其编译为本机代码,但前提是它可以提高性能。请记住,Hotspot 是唯一知道优化是否是好事的组件。

          另外,请注意 javac 可能会进行一项优化:它消除了死代码。考虑这个类:

          public class Test {
          public final static boolean ENABLED=false;
          
          public static void main(String... args) { 
            if(ENABLED)
              System.out.println("Hello World");
            }
          }
          

          main 方法的编译字节码是这样的:

          public static void main(java.lang.String[]);
            Code:
             0:   return
          

          => javac 检测到无法到达println 行并将其删除。

          【讨论】:

            猜你喜欢
            • 2014-03-31
            • 1970-01-01
            • 2010-09-16
            • 2016-04-27
            • 1970-01-01
            • 1970-01-01
            • 2012-10-01
            • 1970-01-01
            • 2021-02-02
            相关资源
            最近更新 更多