Java编译器对重复方法调用的优化?

发布于 2024-09-12 22:25:07 字数 446 浏览 1 评论 0原文

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

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

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

任何见解都将不胜感激;我把这个问题带到了疯狂的地步(我认为我的计算机在一段时间内速度非常快,所以我继续添加方法调用,直到在 43671 行出现代码太大错误)。

Does the java compiler (the default javac that comes in JDK1.6.0_21) optimize code to prevent the same method from being called with the same arguments over and over? If I wrote this code:

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

Would the method foo(bar) only run once? If so, is there any way to prevent this optimization? (I'm trying to compare runtime for two algos, one iterative and one comparative, and I want to call them a bunch of times to get a representative sample)

Any insight would be much appreciated; I took this problem to the point of insanity (I though my computer was insanely fast for a little while, so I kept on adding method calls until I got the code too large error at 43671 lines).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

⒈起吃苦の倖褔 2024-09-19 22:25:07

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

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

The optimization you are observing is probably nothing to do with repeated calls ... because that would be an invalid optimization. More likely, the optimizer has figured out that the method calls have no observable effect on the computation.

The cure is to change the method so that it does affect the result of computation ...

浅浅淡淡 2024-09-19 22:25:07

事实并非如此;如果 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);
    }
}

It doesn't; that would cause a big problem if foo is non-pure (changes the global state of the program). For example:

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);
    }
}
终止放荡 2024-09-19 22:25:07

您没有提供足够的信息来提供任何明确的答案,但 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++ 的相同优化使得基准测试变得非常困难。

You haven't provided enough information to allow for any definitive answers, but the jvm runtime optimizer is extremely powerful and does all sorts of inlining, runtime dataflow and escape analysis, and all manner of cache tricks.

The end result is to make the sort of micro-benchmarks you are trying to perform all but useless in practice; and extremely difficult to get right even when they are potentially useful.

Definitely read http://www.ibm.com/developerworks/java/library/j-benchmark1.html for a fuller discussion on the problems you face. At the very least you need to ensure:

  1. foo is called in a loop that runs thousands of times
  2. foo() returns a result, and
  3. that result is used

The following is the minimum starting point, assuming foo() is non-trivial and therefore is unlikely to be inlined. Note: You still have to expect loop-unrolling and other cache level optimizations. Also watch out for the hotspot compile breakpoint (I believe this is ~5000 calls on -server IIRC), which can completely stuff up your measurements if you try to re-run the measurements in the same 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);
    }
}

Seriously, you need to do some reading before you can make any meaningful progress towards writing benchmarks on a modern JVM. The same optimizations that allows modern Java code to match or even sometimes beat C++ make benchmarking really difficult.

゛清羽墨安 2024-09-19 22:25:07

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

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

如果您觉得您的算法太快,请尝试给他们一些大型或复杂的问题集。只有少数算法总是相当快。

The Java compiler is not allowed to perform such optimizations because method calls very likely cause side effets, for example IO actions or changes to all fields it can reach, or calling other methods that do so.

In functional languages where each function call is guaranteed to return the same result if called with the same arguments (changes to state are forbidden), a compiler might indeed optimize away multiple calls by memorizing the result.

If you feel your algorithms are too fast, try to give them some large or complicated problem sets. There are only a few algorithms which are always quite fast.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文