【发布时间】:2019-02-10 03:53:20
【问题描述】:
我对来自 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 检查,它也显示在 profiler 上,惊讶地发现 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。部署后,我观察到延迟实际上增加了
是不是因为 foo 之前是直接方法调用,一旦我使用接口引用,调度虚拟 foo 的额外间接是我在延迟中看到的开销(接口方法调用的开销远大于 Nan 检查本身??) ? jvm编译器不能内联这个接口方法吗?
【问题讨论】:
-
上面的解决方案一点用处都没有。关于延迟增加的原因或我可以尝试哪些其他方法来进一步了解 jvm/jit 正在做什么的任何见解都会更有益。
-
给定主题不是解决方案,它描述了它的行为方式。所以你可能会理解这些限制。至少它说没有编译器或运行时会将非最终方法设为内联。它说所有优化都将由编译器和运行时完成(取决于编译器和我们使用的 JRE)。使用 final 方法,静态方法会加速你的应用程序。
-
将
res = do some computation;提取到方法中可能会减小foo_old的大小并使foo_old的内联成为可能。然后 JVM 可能能够自行消除isNaN检查。 +++ 我看不出使用接口有什么帮助(但我可能错了)。 -
@maaartinus :做一些计算是非常简单的 1-2 浮点比较。所以我不认为提取对内联有任何影响,因为代码更小。代码已经太小了。第二个 foo_old 没有变小,它是一样的, foo_optimized 没有 nan 检查。你能详细说明为什么 jvm 可以自己摆脱 nan check 吗?我没有看到 jvm 能够做到这一点的任何方式,因为它取决于运行时数据(我看不到 jvm 运行时可以通过代码分析甚至使用运行时信息来检测或确定该值的方式)。
标签: java performance interface profiling virtual-functions