Why is “while (i++ < n) {}” significantly slower than “while (++i < n) {}”

后端 未结 5 2133
长发绾君心
长发绾君心 2020-12-04 14:16

Apparently on my Windows 8 laptop with HotSpot JDK 1.7.0_45 (with all compiler/VM options set to default), the below loop

final int n = Integer.MAX_VALUE;
in         


        
5条回答
  •  渐次进展
    2020-12-04 14:46

    EDIT 2

    You should really look here:

    http://hg.openjdk.java.net/code-tools/jmh/file/f90aef7f1d2c/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_11_Loops.java

    EDIT The more I think about it, I realise that this test is somehow wrong, the loop will get seriously optimized by the JVM.

    I think that you should just drop the @Param and let n=2.

    This way you will test the performance of the while itself. The results I get in this case :

    o.m.t.WhileTest.testFirst      avgt         5        0.787        0.086    ns/op
    o.m.t.WhileTest.testSecond     avgt         5        0.782        0.087    ns/op
    

    The is almost no difference

    The very first question you should ask yourself is how you test and measure this. This is micro-benchmarking and in Java this is an art, and almost always a simple user (like me) will get the results wrong. You should rely on a benchmark test and very good tool for that. I used JMH to test this:

        @Measurement(iterations=5, time=1, timeUnit=TimeUnit.MILLISECONDS)
    @Fork(1)
    @Warmup(iterations=5, time=1, timeUnit=TimeUnit.SECONDS)
    @OutputTimeUnit(TimeUnit.NANOSECONDS)
    @BenchmarkMode(Mode.AverageTime)
    @State(Scope.Benchmark)
    public class WhileTest {
        public static void main(String[] args) throws Exception {
            Options opt = new OptionsBuilder()
                .include(".*" + WhileTest.class.getSimpleName() + ".*")
                .threads(1)
                .build();
    
            new Runner(opt).run();
        }
    
    
        @Param({"100", "10000", "100000", "1000000"})
        private int n;
    
        /*
        @State(Scope.Benchmark)
        public static class HOLDER_I {
            int x;
        }
        */
    
    
        @Benchmark
        public int testFirst(){
            int i = 0;
            while (++i < n) {
            }
            return i;
        }
    
        @Benchmark
        public int testSecond(){
            int i = 0;
            while (i++ < n) {
            }
            return i;
        }
    }
    

    Someone way more experienced in JMH might correct this results (I really hope so!, since I am not that versatile in JMH yet), but the results show that the difference is pretty darn small:

    Benchmark                        (n)   Mode   Samples        Score  Score error    Units
    o.m.t.WhileTest.testFirst        100   avgt         5        1.271        0.096    ns/op
    o.m.t.WhileTest.testFirst      10000   avgt         5        1.319        0.125    ns/op
    o.m.t.WhileTest.testFirst     100000   avgt         5        1.327        0.241    ns/op
    o.m.t.WhileTest.testFirst    1000000   avgt         5        1.311        0.136    ns/op
    o.m.t.WhileTest.testSecond       100   avgt         5        1.450        0.525    ns/op
    o.m.t.WhileTest.testSecond     10000   avgt         5        1.563        0.479    ns/op
    o.m.t.WhileTest.testSecond    100000   avgt         5        1.418        0.428    ns/op
    o.m.t.WhileTest.testSecond   1000000   avgt         5        1.344        0.120    ns/op
    

    The Score field is the one you are interested in.

提交回复
热议问题