2014-07-25 36 views
7

执行Java REST服务性能测试我看到一个意外的模式:在每次调用中创建并返回始终相同的值对象的方法运行得比另一个只返回存储在类或对象字段中的值对象的版本快。什么JVM优化导致这些性能结果?

代码:

@POST @Path("inline") public Response inline(String s) { 
    return Response.status(Status.CREATED).build(); 
}  

private static final Response RESP = Response.status(Status.CREATED).build(); 
@POST @Path("staticfield") public Response static(String s) { 
    return RESP; 
} 

private final Response resp = Response.status(Status.CREATED).build(); 
@POST @Path("field") public Response field(String s) { 
    return resp; 
} 

字节代码:

  • 内联(较快):getstatic,invokestatic,invokevirtual,areturn
  • 静态日提交的(更慢):getstatic,areturn
  • 对象字段(较慢):aload,getfield,areturn

性能(使用Apache AB,单个线程,几个与一致的结果运行):

  • 内联:17078.29 [#/秒](平均)
  • 静态字段:5242.64 [# /秒](平均)
  • 对象字段:5417.40 [#/秒](平均)

环境:RHEL6 + JDK Oracle 1.7.0_60-b19 64位

JVM是否有可能使用本地代码优化了内联版本,但从来没有考虑优化其他两个版本,因为它们已经很小了?

+0

我认为在上述代码之外最有可能的是某些东西不像您认为的那样工作。 –

+0

“返回总是相同的值对象”......也许REST层知道结果可以被缓存呢? –

+1

发布完整的可编译基准。只有这样我们才能深入了解发生了什么。 – tmyklebu

回答

3

正如在评论中指出的,很难说实际上没有看到装配。由于同期使用的是REST框架,不过我认为从程序集中很难判断出有相当多的代码需要阅读。

相反,我想给你一个有教养的猜测,因为你的代码是一个应用costant folding的原型示例。当一个值被内联并且不从字段读取时,JVM可以安全地假定这个值是常量。当JIT编译该方法时,常量表达式因此可以安全地与您的框架代码合并,从而可能导致组装JIT的不足,从而提高性能。对于字段值,即使是final,也不能假定该字段值可以更改。 (只要字段值不是编译时间常量,原始或常量String,它们由javac内联。)因此,JVM可能不会不断折叠该值。

可以read more on constant folding教程到JMH它指出:

如果JVM实现计算的结果是相同的,无论是什么,它可以巧妙地优化它。在我们的例子中,这意味着我们可以将计算移到内部JMH循环之外。通过始终读取状态的输入,根据该状态计算结果并遵循防止DCE的规则,可以防止这种情况发生。

我希望你使用了这样的框架。否则,您的绩效指标不太可能有效。

通过读取字节码,您通常可能无法了解运行时性能,因为JIT编译器可以在优化期间将字节码调整为任何值。字节代码布局应该只在代码被解释时才重要,它通常不是测试性能的状态,因为性能至关重要,代码始终是JIT编译的。

+0

这是迄今为止最好的答案。我重复了没有框架的性能测试,并且内联方法更慢(相反的结果)。所以,无论优化如何,只有在框架代码处于活动状态时才会发生。正如你所说,很难确定这是真正发生在这种情况下的事情,但是你的解释很好地适合了上下文,并且由于你的回应,我今天学到了新的东西。谢谢! – Sebastian

相关问题