在我们的一个 java 应用程序中,我们有相当多的协议缓冲区类,并且 jar 本质上公开了一个接口,其中包含另一个应用程序使用的一种方法。我们注意到,第一次调用此方法时,调用时间非常长(>500ms),而后续调用则要快得多(
当在不同的应用程序中,工作方式完全不同 - 但也使用协议缓冲区 - 显示相同的行为时,这一点得到了进一步证实。此外,我们尝试创建一个虚拟实例(XY.newBuilder().build()
)在启动时所有原型缓冲区类以及我们添加的每一个缓冲区类中,我们都可以注意到第一次调用的开销下降。
在.NET中我可以找到另一个显示类似问题的问题(为什么 ProtoBuf 在第一次调用时如此慢,但在循环内却非常快? https://stackoverflow.com/questions/13735248/why-is-protobuf-so-slow-on-the-1st-call-but-very-fast-inside-loops),但是那里的解决方案似乎特定于具有预编译序列化器的 C#。到目前为止我在Java中还没有找到同样的问题。是否有像上面问题中所示的适用于 java 的解决方法?
JVM 附带即时 (JIT) 编译器,它可以对您的代码进行大量优化。如果您想进一步了解 JVM 内部结构,您可以深入了解它。会有类加载和卸载、性能分析、代码编译和反编译、偏向锁定等。
给你一个例子,这会变得多么复杂,根据本文 https://www.infoq.com/articles/OpenJDK-HotSpot-What-the-JIT/,在 OpenJDK 中,有两个编译器(C1 和 C2),具有五种可能的代码编译层:
分层编译有五层优化。它从第 0 层(解释器层)开始,其中仪表提供有关性能关键方法的信息。很快,第一层,即简单的 C1(客户端)编译器,就优化了代码。在第一层,没有分析信息。接下来是第 2 层,其中仅编译少数方法(同样由客户端编译器编译)。在第 2 层,对于这几种方法,收集入口计数器和环回分支的分析信息。然后,第 3 层将看到客户端编译器编译的所有方法以及完整的分析信息,最后第 4 层将利用服务器编译器 C2。
这里的要点是,如果您需要可预测的性能,您应该始终通过在每次部署后运行一些虚拟请求来预热代码。
您使用创建所有使用的 protobuf 对象的虚拟代码做了正确的事情,但您应该更进一步并预热您正在使用的实际方法。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)