Java compiler optimization for repeated method cal

2019-04-25 11:29发布

问题:

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).

回答1:

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 ...



回答2:

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);
    }
}


回答3:

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.



回答4:

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.