【问题标题】:Is there a way to tell JVM to optimize my code before processing?有没有办法告诉 JVM 在处理之前优化我的代码?
【发布时间】:2011-06-05 07:26:38
【问题描述】:

我有一个方法,第一次执行需要很长时间。但是经过多次调用,它需要的时间减少了大约 30 倍。所以,为了让我的应用程序更快地响应用户交互,我用一些关于应用程序初始化的示例数据“预热”了这个方法(5 次)。但这会增加应用程序的启动时间。
我读到,JVM 可以优化我的 java 代码并将其编译为本机代码,从而加快速度。 我想知道 - 也许有某种方法可以明确告诉 JVM 我希望在应用程序启动时编译此方法?

【问题讨论】:

  • 你真的确定它在被多次调用后性能更好,正是因为 JVM 优化。根据您的方法的作用,它也可能是其他东西 - 例如,磁盘或数据库缓存......
  • @Rogach:你不能先优化你的方法吗?它是做什么的?
  • 这个方法具体是做什么的?
  • 这个方法太大了,把它贴在这里,但基本上它需要像 Path2D 这样的集合,并根据包含另一个路径的路径将它们组合在一起。其中最长的部分是检查形状的自相交,这是使用 Shamos-Hoey 算法完成的。执行速度似乎不是基于chaching——我可以给方法各种样本数据集(以及各种实际数据),效果还是一样的——方法在执行后变得更快。跨度>
  • pastebin.com/j5RAxs0s - 这是我的应用程序的两个源文件。有问题的方法是 Litera.prepareLiteras()

标签: java optimization jvm


【解决方案1】:

JVM 在运行时进行 JiT(即时)优化。这意味着它可以分析代码的执行方式并进行优化以提高运行时性能。这发生在运行时。如果您发现该方法在执行几次后变得更快,这可能是因为 JiT 优化(除非您的分析存在缺陷,例如,由于数据变得更简单,该方法变得更快)。如果您的分析是正确的,那么编译到本机实际上可能会伤害您,因为您将不再获得运行时优化。

我们可以看看方法吗?您也许可以让它更快,而不必担心 JVM 是如何工作的。您应该准确地隔离最昂贵的操作所在的位置。您还应该验证这不是某种垃圾收集问题。即,也许该方法很好,但是正在进行的 GC 正在消耗时间,当它完成时,您的方法以可接受的速度运行。

【讨论】:

  • 关于第二段,有没有一个好的工具来分析Java程序的执行?例如,像 Shark 之类的东西(afaik,Shark!仅适用于 Macintosh,并且适用于“类 C”代码)
  • @posdef,我刚刚搜索了“最佳免费 Java 分析器”并得到 stackoverflow.com/questions/163722/…
【解决方案2】:
【解决方案3】:

JIT 优化之所以如此出色,正是因为它们优化了您的代码实际 所做的事情,而不是它在不同情况下可以 做的事情。

由于输入数据不同,JITted 代码甚至可能在不同的运行中有所不同。甚至可以在情况发生变化时多次重新优化。

换句话说:没有真实数据,JVM 无法很好地优化代码。 (即它只能做“静态”优化)

但最后,如果你得到如此高的改进(30 倍是很多!),很可能是两者之一

  • 不是代码,而是其他东西(如文件或数据库缓存)
  • 非常 源代码级别的非最佳代码。 (比如一些繁重的计算可能会超出紧密的循环)

编辑:

查看您的代码后,在Literas.prepareLiteras() 上的一个大循环中,您不断地调用path.contains(p),具有不同的点但路径相同。 SimplePath.contains() 每次调用时都会创建一个边界形状,因此您最终会一次又一次地创建相同的形状。这是应该从内部循环中拉出的一个典型例子。

我不认为 JIT 可以优化整个方法,但在某些极端情况下,它可能会将 getShape() 转换为专门用于单个路径的东西,然后为下一个路径重新编译。不能很好地利用 JVM 智能,是吗?

【讨论】:

    【解决方案4】:

    如果您使用 Sun JVM,则 JIT 编译的阈值不同,具体取决于您使用的是客户端 JVM 还是服务器 JVM。对于客户端,它是对方法的 1500 次调用,对于服务器,它是 10000。您可以使用 JVM 参数 -XX:CompileThreshold=100 将其更改为非常低的值。

    不过,如此低的门槛不会有利于您的全球表现。我只建议使用它来测试通过预热带来的性能提升是否受到 JIT 的影响。

    由于 JIT 优化,我从未见过通过热身获得 30 倍的改进。然而。总是因为一些缓存。

    【讨论】:

    • 哇。它几乎杀死了应用程序。是的,似乎改进不是 JIT 的结果。该参数实际上使事情变慢了。
    • 好吧,然后试试探查器。当前的 JDK 包括 JVisualVM,它带有一个基本的分析器。
    【解决方案5】:

    如果您有 64 位操作系统,您可以尝试在 64 位 JVM 上运行它。

    Oracle 的实现中有两个版本的 JVM:客户端 VM 和服务器 VM。在 32 位 Windows 上,客户端 VM 是默认设置。在 64 位 Windows 上,服务器 VM 是默认设置。

    客户端和服务器虚拟机之间的区别在于它们的调整方式:服务器虚拟机比客户端虚拟机进行更积极的优化(并且更早地进行优化)。服务器 VM 已针对长时间运行的进程优化设置。客户端 VM 具有针对桌面使用进行了优化的默认设置:它预先进行的优化较少,但启动速度更快。

    我在计算密集型程序中遇到了重大的速度差异;这些有时在 64 位 JVM 上的运行速度是 32 位 JVM 的两倍。

    【讨论】:

      【解决方案6】:

      大多数情况下,我会使用 hvgotcodes,但问题也可能不是 JVM 优化,而是在前几次运行后,来自磁盘的数据现在在缓存中,或者前几次仍在加载并初始化类,但之后它们都在内存中。

      【讨论】:

        猜你喜欢
        • 2011-07-31
        • 1970-01-01
        • 1970-01-01
        • 2011-01-11
        • 1970-01-01
        • 1970-01-01
        • 2010-09-09
        • 2015-02-25
        相关资源
        最近更新 更多