7

java 编译器(JDK1.6.0_21 中的默认 javac)是否优化代码以防止使用相同的参数一遍又一遍地调用相同的方法?如果我写了这段代码:

public class FooBar {
    public static void main(String[] args) {
        foo(bar);
        foo(bar);
        foo(bar);
    }
}

该方法foo(bar)是否只运行一次?如果是这样,有什么办法可以防止这种优化?(我正在尝试比较两种算法的运行时间,一种是迭代的,一种是比较的,我想多次调用它们以获得具有代表性的样本)

任何见解将不胜感激;我把这个问题带到了精神错乱的地步(虽然我的电脑有一段时间非常快,所以我继续添加方法调用,直到我code too large在 43671 行得到错误)。

4

4 回答 4

6

您正在观察的优化可能与重复调用无关......因为那将是无效的优化。更有可能的是,优化器已经发现方法调用对计算没有明显的影响。

解决方法是改变方法,使其确实影响计算结果......

于 2010-08-02T03:17:30.867 回答
4

它没有;foo如果是非纯的(改变程序的全局状态),那将导致一个大问题。例如:

public class FooBar {
    private int i = 0;
    private static int foo() {
        return ++i;
    }

    public static void main(String[] args) {
        foo();
        foo();
        foo();
        System.out.println(i);
    }
}
于 2010-08-02T03:17:05.407 回答
4

您没有提供足够的信息来提供任何明确的答案,但是 jvm 运行时优化器非常强大,并且可以执行各种内联、运行时数据流和转义分析,以及各种缓存技巧。

最终结果是使您尝试执行的那种微基准测试在实践中几乎毫无用处;即使它们可能有用,也很难做到正确。

一定要阅读http://www.ibm.com/developerworks/java/library/j-benchmark1.html以更全面地讨论您面临的问题。至少您需要确保:

  1. foo 在运行数千次的循环中被调用
  2. foo() 返回一个结果,并且
  3. 使用该结果

以下是最低起点,假设 foo() 是不平凡的,因此不太可能被内联。注意:您仍然必须期待循环展开和其他缓存级别的优化。还要注意热点编译断点(我相信这是 -server IIRC 上的约 5000 次调用),如果您尝试在同一个 JVM 中重新运行测量,它可能会完全填满您的测量。

public class FooBar {
    public static void main(String[] args) {
        int sum = 0;
        int ITERATIONS = 10000;
        for (int i = 0; i < ITERATIONS; i++) {
            sum += foo(i);
        }

        System.out.println("%d iterations returned %d sum", ITERATIONS, sum);
    }
}

说真的,您需要先阅读一些内容,然后才能在现代 JVM 上编写基准测试取得任何有意义的进展。允许现代 Java 代码匹配甚至有时击败 C++ 的相同优化使得基准测试变得非常困难。

于 2010-08-02T04:00:49.793 回答
0

Java 编译器不允许执行此类优化,因为方法调用很可能会导致副作用,例如 IO 操作或对其可以到达的所有字段的更改,或调用其他这样做的方法。

在函数式语言中,如果使用相同的参数调用每个函数调用都保证返回相同的结果(禁止更改状态),编译器确实可以通过记忆结果来优化多次调用。

如果你觉得你的算法太快了,试着给他们一些的或复杂的问题集。只有少数算法总是很快。

于 2011-08-10T09:18:42.787 回答