JMH – Java Forward loop vs Reverse loop

A JMH benchmark test about Forward loop vs Reverse loop for a List. There is a myth about reverse loop is faster, is this true?

Tested with

  • JMH 1.21
  • Java 10
  • Maven 3.6
  • CPU i7-7700

Forward loop


	for (int i = 0; i < aList.size(); i++) {
		String s = aList.get(i);
		System.out.println(s);
	}

Reverse loop


	for (int i = aList.size() - 1; i >= 0; i--) {
		String s = aList.get(i);
		System.out.println(s);
	}

1. Forward loop vs Reverse loop

Loop a List containing 1 million and 10 million String objects.

BenchmarkForwardReverseLoop.java

package com.mkyong.benchmark;

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(value = 1, jvmArgs = {"-Xms2G", "-Xmx2G"})
@Warmup(iterations = 5)
@Measurement(iterations = 10)
@State(Scope.Benchmark)
public class BenchmarkForwardReverseLoop {

    @Param({"1000000", "10000000"})
    private int N;

    private List<String> DATA_FOR_TESTING = createData();

    public static void main(String[] argv) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(BenchmarkForwardReverseLoop.class.getSimpleName())
                .forks(1)
                .build();

        new Runner(opt).run();
    }

    @Setup
    public void setup() {
        DATA_FOR_TESTING = createData();
    }

    @Benchmark
    public void forwardLoop(Blackhole bh) {
        for (int i = 0; i < DATA_FOR_TESTING.size(); i++) {
            String s = DATA_FOR_TESTING.get(i);
            bh.consume(s);
        }
    }

    @Benchmark
    public void reverseLoop(Blackhole bh) {
        for (int i = DATA_FOR_TESTING.size() - 1; i >= 0; i--) {
            String s = DATA_FOR_TESTING.get(i);
            bh.consume(s);
        }
    }

    private List<String> createData() {
        List<String> data = new ArrayList<>();
        for (int i = 0; i < N; i++) {
            data.add("Number : " + i);
        }
        return data;
    }

}

2. Result


$ java -jar target\benchmarks.jar BenchmarkForwardReverseLoop

# JMH version: 1.21
# VM version: JDK 10.0.1, Java HotSpot(TM) 64-Bit Server VM, 10.0.1+10
# VM invoker: C:\Program Files\Java\jre-10.0.1\bin\java.exe
# VM options: -Xms2G -Xmx2G
# Warmup: 5 iterations, 10 s each
# Measurement: 10 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.mkyong.benchmark.BenchmarkForwardReverseLoop.forwardLoop
# Parameters: (N = 1000000)

# Run progress: 0.00% complete, ETA 00:10:00
# Fork: 1 of 1
# Warmup Iteration   1: 5.333 ms/op
# Warmup Iteration   2: 5.300 ms/op
# Warmup Iteration   3: 5.210 ms/op
# Warmup Iteration   4: 5.241 ms/op
# Warmup Iteration   5: 5.236 ms/op
Iteration   1: 5.127 ms/op
Iteration   2: 5.089 ms/op
Iteration   3: 5.110 ms/op
Iteration   4: 5.148 ms/op
Iteration   5: 5.233 ms/op
Iteration   6: 5.156 ms/op
Iteration   7: 5.160 ms/op
Iteration   8: 5.605 ms/op
Iteration   9: 5.169 ms/op
Iteration  10: 5.130 ms/op


Result "com.mkyong.benchmark.BenchmarkForwardReverseLoop.forwardLoop":
  5.193 ±(99.9%) 0.227 ms/op [Average]
  (min, avg, max) = (5.089, 5.193, 5.605), stdev = 0.150
  CI (99.9%): [4.966, 5.419] (assumes normal distribution)


# JMH version: 1.21
# VM version: JDK 10.0.1, Java HotSpot(TM) 64-Bit Server VM, 10.0.1+10
# VM invoker: C:\Program Files\Java\jre-10.0.1\bin\java.exe
# VM options: -Xms2G -Xmx2G
# Warmup: 5 iterations, 10 s each
# Measurement: 10 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.mkyong.benchmark.BenchmarkForwardReverseLoop.forwardLoop
# Parameters: (N = 10000000)

# Run progress: 25.00% complete, ETA 00:07:31
# Fork: 1 of 1
# Warmup Iteration   1: 61.361 ms/op
# Warmup Iteration   2: 61.189 ms/op
# Warmup Iteration   3: 61.570 ms/op
# Warmup Iteration   4: 61.662 ms/op
# Warmup Iteration   5: 61.604 ms/op
Iteration   1: 61.010 ms/op
Iteration   2: 61.066 ms/op
Iteration   3: 62.129 ms/op
Iteration   4: 61.854 ms/op
Iteration   5: 61.248 ms/op
Iteration   6: 61.104 ms/op
Iteration   7: 64.452 ms/op
Iteration   8: 62.398 ms/op
Iteration   9: 62.766 ms/op
Iteration  10: 66.125 ms/op


Result "com.mkyong.benchmark.BenchmarkForwardReverseLoop.forwardLoop":
  62.415 ±(99.9%) 2.535 ms/op [Average]
  (min, avg, max) = (61.010, 62.415, 66.125), stdev = 1.677
  CI (99.9%): [59.880, 64.950] (assumes normal distribution)


# JMH version: 1.21
# VM version: JDK 10.0.1, Java HotSpot(TM) 64-Bit Server VM, 10.0.1+10
# VM invoker: C:\Program Files\Java\jre-10.0.1\bin\java.exe
# VM options: -Xms2G -Xmx2G
# Warmup: 5 iterations, 10 s each
# Measurement: 10 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.mkyong.benchmark.BenchmarkForwardReverseLoop.reverseLoop
# Parameters: (N = 1000000)

# Run progress: 50.00% complete, ETA 00:05:02
# Fork: 1 of 1
# Warmup Iteration   1: 6.220 ms/op
# Warmup Iteration   2: 5.861 ms/op
# Warmup Iteration   3: 5.955 ms/op
# Warmup Iteration   4: 5.919 ms/op
# Warmup Iteration   5: 6.142 ms/op
Iteration   1: 5.840 ms/op
Iteration   2: 5.864 ms/op
Iteration   3: 5.892 ms/op
Iteration   4: 6.212 ms/op
Iteration   5: 5.911 ms/op
Iteration   6: 5.906 ms/op
Iteration   7: 6.055 ms/op
Iteration   8: 6.210 ms/op
Iteration   9: 6.095 ms/op
Iteration  10: 5.821 ms/op


Result "com.mkyong.benchmark.BenchmarkForwardReverseLoop.reverseLoop":
  5.980 ±(99.9%) 0.227 ms/op [Average]
  (min, avg, max) = (5.821, 5.980, 6.212), stdev = 0.150
  CI (99.9%): [5.754, 6.207] (assumes normal distribution)


# JMH version: 1.21
# VM version: JDK 10.0.1, Java HotSpot(TM) 64-Bit Server VM, 10.0.1+10
# VM invoker: C:\Program Files\Java\jre-10.0.1\bin\java.exe
# VM options: -Xms2G -Xmx2G
# Warmup: 5 iterations, 10 s each
# Measurement: 10 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.mkyong.benchmark.BenchmarkForwardReverseLoop.reverseLoop
# Parameters: (N = 10000000)

# Run progress: 75.00% complete, ETA 00:02:30
# Fork: 1 of 1
# Warmup Iteration   1: 55.012 ms/op
# Warmup Iteration   2: 54.479 ms/op
# Warmup Iteration   3: 54.497 ms/op
# Warmup Iteration   4: 54.677 ms/op
# Warmup Iteration   5: 54.287 ms/op
Iteration   1: 54.361 ms/op
Iteration   2: 54.432 ms/op
Iteration   3: 54.367 ms/op
Iteration   4: 54.518 ms/op
Iteration   5: 54.497 ms/op
Iteration   6: 54.369 ms/op
Iteration   7: 54.698 ms/op
Iteration   8: 54.393 ms/op
Iteration   9: 54.377 ms/op
Iteration  10: 54.675 ms/op


Result "com.mkyong.benchmark.BenchmarkForwardReverseLoop.reverseLoop":
  54.469 ±(99.9%) 0.193 ms/op [Average]
  (min, avg, max) = (54.361, 54.469, 54.698), stdev = 0.127
  CI (99.9%): [54.276, 54.661] (assumes normal distribution)


# Run complete. Total time: 00:10:04

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark                                     (N)  Mode  Cnt   Score   Error  Units
BenchmarkForwardReverseLoop.forwardLoop   1000000  avgt   10   5.193 ± 0.227  ms/op
BenchmarkForwardReverseLoop.forwardLoop  10000000  avgt   10  62.415 ± 2.535  ms/op
BenchmarkForwardReverseLoop.reverseLoop   1000000  avgt   10   5.980 ± 0.227  ms/op
BenchmarkForwardReverseLoop.reverseLoop  10000000  avgt   10  54.469 ± 0.193  ms/op

Result : The normal forward loop is faster in the 1 million loops, but for 10 million loops, the reverse loop is faster. However, the difference isn't that significant.

Download Source Code

$ git clone https://github.com/mkyong/jmh-benchmark
$ mvn package
$ java -jar target\benchmarks.jar BenchmarkForwardReverseLoop

References

  1. Java - Reverse loop versus Forward loop
  2. Java JMH Benchmark Tutorial
  3. Why reverse loops are not faster

About the Author

author image
mkyong
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter, or befriend him on Facebook or Google Plus. If you like my tutorials, consider make a donation to these charities.

Comments

avatar