有关可能的Java(或其他内存管理语言)优化的问题

时间:2010-07-12 11:40:43

标签: compilation jit aot

从我所看到的java(通常)似乎编译java到非常(完全没有?)优化的java字节码,留给jit进行优化。这是真的?如果有的话,有任何探索(可能在替代实现中)让编译器优化代码,这样jit就可以做更少的工作了(这可能吗?)

此外,许多人似乎不喜欢Java(以及许多其他高级内存管理语言)的本机代码生成(有时称为提前编译),原因很多,例如可移植性丢失(等等)。 ),但也部分是因为(至少对那些具有及时编译器的语言而言)认为,提前编译到机器代码将错过jit编译器可能完成的可能优化,因此可能会更慢从长远来看。

这让我想知道是否有人试图实现http://en.wikipedia.org/wiki/Profile-guided_optimization(编译成二进制+一些额外的内容然后运行程序并分析测试运行的运行时信息以生成有希望的更优化的二进制文件java /(其他内存管理语言)的世界用法)以及它与jit代码的比较方式?任何人都有线索?

3 个答案:

答案 0 :(得分:4)

就我个人而言,我认为最大的区别不在于JIT编译和AOT编译,而是在类编译和整个程序优化之间。

运行javac时,它只查看单个.java文件,将其编译为单个.class文件。检查所有接口实现以及虚拟方法和覆盖的有效性但未解决(因为在不分析整个程序的情况下无法知道真正的方法调用目标)。

JVM使用“运行时加载和链接”将所有类组装成一个连贯的程序(程序中的任何类都可以调用专门的行为来更改默认的加载/链接行为)。

但是,在运行时,JVM可以删除绝大多数虚拟方法。它可以内联所有的getter和setter,将它们转换为原始字段。当这些原始字段被内联时,它可以执行常量传播以进一步优化代码。 (在运行时,没有私有字段。)如果只有一个线程在运行,JVM可以消除所有同步原语。

总而言之,如果不分析整个程序,有很多优化是不可能的,而且整个程序分析的最佳时间是在运行时。

答案 1 :(得分:2)

配置文件引导优化有一些注意事项,其中一个甚至在您链接的Wiki文章中也提到过。它的结果是有效的

  • 表示给定样本,代表用户实际使用代码的方式或其他代码。
  • 用于给定平台(CPU,内存+其他硬件,操作系统,等等) 从性能的角度来看,即使在通常被认为(或多或少)相同的平台之间也存在相当大的差异(例如,将单个核心,旧Athlon与512M与6核Intel与8G进行比较,在Linux上运行,但与非常不同的内核版本)。
  • 用于给定的JVM及其配置。

如果其中任何一个发生变化,那么您的分析结果(以及基于它们的优化)将不再有效。最有可能的一些优化仍然会产生有益效果,但其中一些可能会导致次优(甚至性能下降)。

正如前面提到的那样,JIT JVM做了一些非常类似于分析的东西,但它们是在动态执行的。它也被称为“热点”,因为它不断监视执行的代码,查找频繁执行的热点,并尝试仅优化这些部分。在这一点上,它将能够利用更多关于代码的知识(了解它的上下文,如何被其他类使用等),所以 - 正如你和其他答案所提到的那样 - 它可以做更好的优化静态的。它将继续监控,如果需要,它将在稍后进行另一轮优化,这次尝试更加努力(寻找更多,更昂贵的优化)。
处理现实生活中的数据(使用统计数据+平台+配置),可以避免前面提到的警告。

它的价格是需要花费在“分析”+ JIT-ing上的额外时间。大多数时候它花得很好。

我猜一个配置文件引导的优化器仍然可以与它竞争(甚至打败它),但只有在某些特殊情况下,如果你能避免这些警告:

  • 您确信您的样本很好地代表了真实生活场景,并且在执行过程中它们不会发生太大变化。
  • 您可以非常准确地了解您的目标平台,并可以对其进行分析。
  • 当然你知道/控制JVM及其配置。

它很少会发生,我猜一般JIT会给你更好的结果,但我没有证据。

如果你的目标是无法进行JIT优化的JVM,那么从配置文件引导优化中获取价值的另一种可能性(我认为大多数小型设备都有这样的JVM)。

BTW其他答案中提到的一个缺点是很容易避免:如果静态/配置文件引导优化很慢(可能就是这种情况),那么只针对发布(或RCs转向测试人员)或夜间构建(仅适用于测试人员)时间不重要的事。) 我认为更大的问题是拥有良好的样本测试用例。创建和维护它们通常并不容易,并且需要花费大量时间。特别是如果你想能够自动执行它们,这在这种情况下非常重要。

答案 2 :(得分:1)

官方Java Hot Spot编译器在运行时执行“自适应优化”,这与您提到的配置文件引导优化基本相同。这至少是这个特定Java实现的一个特征很长一段时间。

在编译时预先执行更多静态分析或优化传递的权衡取决于您从额外的努力中获得的(不断减少的)返回,而不是编译器运行所需的时间。像MLton(用于标准ML)这样的编译器是一个带有大量静态检查的整个程序优化编译器。它产生非常好的代码,但在中型到大型程序上变得非常非常慢,即使在快速系统上也是如此。

所以Java方法似乎是尽可能地使用JIT和自适应优化,初始编译传递只产生可接受的有效二进制。绝对相反的结局是使用像MLKit这样的方法,它可以对区域和内存行为进行大量静态推理。