Java接口方法内联

时间:2018-09-05 05:34:15

标签: java performance interface profiling virtual-method

我对来自C ++世界的Java还是很陌生。 我正在运行一些服务器代码,该代码运行一个方法foo(),该方法每秒被调用数百万次。这是对延迟敏感的代码,该方法还显示在探查器中,按进程消耗了总体CPU使用量的20%。

int foo_old() {
     if (Float.isNan(this.x)) { // shows up in profiling
        res = do some computation; // some floating point comparison, doesn't show up in profiling;
        return res;
     } else {
        // Happens 99% of the time;
        res = do something else; // some floating point comparison, doesn't show up in profiling;
        return res;
     }
}

有没有一种简便的方法可以测试我的方法foo是否将内联?我可以从探查器堆栈中正在运行的服务器中的跟踪信息中得知吗?

我通过尝试简化方法foo()进行了一些优化。基本上,在foo中有一个float.isNan检查,它也显示在探查器中,惊讶地发现nan检查速度较慢。相较于其他布尔运算(小于,大于浮点比较)更快。

我尝试的一种方法是删除nan check,即因为我在编译时知道某个对象是否需要nan check,所以我尝试存储功能接口(成员变量)并分配此功能接口foo_old(具有nan check的功能) )或根据创建对象时已知的对象属性foo_optimized(不进行nan检查)(在对象的构造函数中,我为此接口分配正确的方法引用。

class A {
  final FuncIf test; // Functional interface with same signature as foo_old, foo_new

  public A(bool optimize) {
      test = optimize ? this::foo_optimized : this::foo_old;
  }

  // same as the original foo mentioned above
  int foo_old() {
    ...
   }

  // No nan check
  int foo_optimized() {
        res = do some computation; 
        return res;
  }
}

现在,当我创建对象时,我知道在编译时/对象构造时要使用哪个版本的foo。所以我将接口变量分配给foo的正确版本。部署后,我发现延迟延迟实际上增加了<10%。即使现在许多对象实际上将使用foo的优化版本。

之所以这样,是因为之前的foo是直接方法调用,并且一旦我使用接口引用,调度虚拟foo的额外间接操作就是我在等待时间中看到的开销(接口方法调用的开销比Nan检查要大得多)本身??)? jvm编译器不能内联此接口方法吗?

1 个答案:

答案 0 :(得分:1)

唯一受过教育的 猜测是测量其bytecode。为此使用javap。基本上JVM有两个编译器C1C2;两者都可以内联该方法。

JVM在内联时会关心三个参数(嗯,这是我所知道的,我也知道还有很多 ):

-XX:MaxInlineSize (35 by default)
-XX:FreqInlineSize (325 by default)
-XX:MinInliningThreshold (250 by default)

如果调用的方法少于MinInliningThreshold(250),则它遵循MaxInlineSize规则,这意味着如果它小于35个字节,它将被内联。如果再多调用它,它将比FreqInlineSize服从,它是325个字节(更多)。

您还可以通过一些参数打印内联或不内联的内容:

-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining

运行这些命令后,您会看到以下消息:

  callee is too large

这是由C1打印的,它告诉您该编译方法超出了MaxInlineSize。或者:

  too big
超过C2时,由MaxInlineSize编译器打印

。或者:

  hot method too big
超过C2时由FreqInlineSize打印