【问题标题】:java compiler optimizationjava编译器优化
【发布时间】:2010-03-06 03:19:14
【问题描述】:

Java 编译器是否足够聪明,可以通过提取以下循环来优化以下循环

Double average = new Double( totalTime / callCount ); 

退出for循环?

public double computeSD( Set values, int callCount, long totalTime ) {
  double diffs = 0.0d; 
  for( Iterator i=values.iterator(); i.hasNext(); ) {
    double value = ( ( Double )i.next() ).doubleValue(); 
    Double average = new Double( totalTime / callCount ); 
    diffs += ( value – average.doubleValue() ) * ( value – average.doubleValue() );
  } 
  double variance = diffs / callCount;
  return Math.sqrt( variance );
}

【问题讨论】:

  • JIT 编译器端的流分析很容易告诉您Double 对象不会在任何地方转义,因此只使用它们包含的值。我敢说上面的功能应该很容易按照 OP 寻求的方式进行优化。
  • 我对此表示怀疑。使用原始双精度,并使 callCount 和 totalTime 最终可能会有所帮助,但我仍然对此表示怀疑。
  • @Thilo:对局部变量使用final 对字节码没有影响。当然,变量的恒常与否是由 JIT 编译器决定的。
  • 为了帮助编译器,让尽可能多的变量为final。据我所知,您可以将 i、value、average、values、callCount、totalTime 和 variance 设为 final。
  • @Chris Dennett:是的,也不是。制作局部变量final 主要是对程序员的帮助,以阻止(禁止)他们重新分配变量(这确实有助于编译器)。但是,如果您实际上只是将每个局部变量分配一次作为一种习惯,那么final 的存在或不存在根本不会改变字节码输出。 (字段的情况有所不同。我确实有一个政策是尽可能多地创建字段final。)

标签: java performance compiler-optimization


【解决方案1】:

没有什么可以阻止字节码编译器(java->bytecode)执行优化。当我在赛门铁克工作时,他们做了一个 Java IDE,编译器编写者确实考虑在我们的编译器中进行一些优化,但表示(在外界)似乎没有人感兴趣,重点是即时 (JIT)编译器与现代 Sun VM 中的 HotSpot 大致相同。

没有什么可以阻止字节码编译器执行优化,但我不知道有什么这样做的。运行时优化非常受关注,但这些在运行时几乎是隐藏的。

因此,source->bytecode 编译器可能不会优化它,但 VM 可能会优化它。如果您使用的是 Android 之类的设备,那么它可能不会执行运行时优化。

【讨论】:

    【解决方案2】:

    Java 不会也不能从循环中提取它。 任何使用“new”关键字都会导致创建一个新对象。 你最好使用Double.valueOf()

    查看Double.valueOf(double)的javadoc:

    "返回一个表示指定 double 值的 Double 实例。如果不需要新的 Double 实例,通常应优先使用此方法而不是构造函数 Double(double),因为此方法可能会产生明显更好的空间和通过缓存频繁请求的值来提高时间性能。”

    如果您使用此方法,它将每次返回相同的对象,从而减少创建的对象数量并提高性能。

    但是,使用valueOf 仍然不是您的答案!

    valueOf 仍然是一个方法调用,并且方法调用不会被优化掉。每次循环迭代都会调用valueOf。查看您的方法并计算方法调用。现在是6,包括hasNextnew Double,类似于方法调用。这些都会每次都发生,并且没有 java 优化会改变这一点。您最好重构以从循环中删除尽可能多的方法调用。

    【讨论】:

      【解决方案3】:

      如果您真的想确定,this question 的答案会告诉您如何查看 JIT 编译器生成的本机代码。

      【讨论】:

        【解决方案4】:

        一开始这似乎是一个明显的优化,但我不这么认为,因为它涉及对象实例化。当然,它是一个不可变的原始盒子类型的实例化,但这仍然不能保证没有副作用。

        我认为当前的任何编译器都无法对此进行优化。要对此进行优化,必须告知编译器某些类具有特殊属性(考虑到将来情况可能会发生变化,这可能是一个危险的提议)。也就是说,编译器必须被告知 API 的细节。这不能仅在语言级别进行优化。

        但是,如果您使用 double,它更有可能得到优化(例如,使用 loop-invariant code motion 技术)。

        【讨论】:

        • JIT 编译器当然可以将某些类型,尤其是java.lang 中的类型视为神奇的。如果没有,我会感到惊讶。
        • 原始类型如何优化?
        【解决方案5】:

        不是真的。编译器只是写出byte-code。如果对代码进行了优化,那就是 Java 虚拟机,这可能取决于平台、实现和执行条件......

        【讨论】:

        • 你确定 Java 编译器没有做明显的优化吗?
        • @Mike:JIT 编译器可以。源代码到字节码的编译器没有,但那是因为它不是要优化字节码的地方,因为无论如何它都必须通过 JIT。
        • @Matthew:阻抗不匹配警报---你和我都在考虑 JIT 编译器,而我认为 Frank 只考虑 javac。 JIT 编译器是进行重量级优化的地方。
        • 好的。所以 JIT 会做优化。基于上面的代码,它将如何优化执行时的字节码?
        • 我记得字节码编译器确实没有执行很多明显的优化。当我为比赛开发 4k 游戏时(我的硬盘崩溃了,我失去了很多进步——啊!)我发现了许多有趣的东西,它们大大减少了源代码的大小。例如,通过将变量放在当前迭代循环之外并重用它们来尝试巧妙地处理变量通常会使代码变得更大。如果有人想看,4k 游戏小程序就在这里:codeknight.net/t4k :)
        猜你喜欢
        • 2011-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-07
        • 1970-01-01
        • 1970-01-01
        • 2014-02-21
        相关资源
        最近更新 更多