首先为自己没有经过严格测试得出的错误结论感到抱歉,原博文,测试完感觉自己发现了一个新bug,后来思前想后觉得不应该是这样的,如果效率差的这么多,jdk的开发人员会不去优化它吗,但是怎么重复测试任然得到一样的结果,非常疑惑。

  我觉得应该是测试方法出问题了,可是怎么也想不到原因,后来了解到jmh,深入研究了一番,觉得jmh的测试值得借鉴,jmh在测试的时候都会先经过预热几遍要测试的代码,示例如下:

public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
            .include(JMHSample_02_BenchmarkModes.class.getSimpleName())
            .warmupIterations(5)
            .measurementIterations(5)
            .forks(1)
            .build();

    new Runner(opt).run();
}

  我觉得这是有必要的,而我的测试恰恰缺少了这个步骤,随后我在测试中应用了预热处理(均是3次预热,5次运行取平均值),得出的以下结论。

  首先是上个测试中被冤枉的java8的foreach循环,测试代码:

package yiwangzhibujian.jmh.test;

import org.openjdk.jmh.annotations.*;
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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@State(Scope.Thread)
@BenchmarkMode(Mode.SingleShotTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class J8Loop {
  public static Map<Integer,List<Dog>> dogMap=new HashMap<>();
  static {
    dogMap.put(10, getDogs(10));
    dogMap.put(100, getDogs(100));
    dogMap.put(1000, getDogs(1000));
    dogMap.put(10000, getDogs(10000));
    dogMap.put(100000, getDogs(100000));
    dogMap.put(1000000, getDogs(1000000));
  }
  
  private static List<Dog> getDogs(int num) {
    List<Dog> dogs=new ArrayList<>();
    for(int i=0;i<num;i++){
      dogs.add(new Dog(i,"dog"+i));
    }
    return dogs;
  }
  
  private void loop(List<Dog> list) {
    list.forEach(dog->{
      dog.hashCode();
    });
  }

  @Benchmark
  @OperationsPerInvocation(10)
  public void measureWrong_10() {
    List<Dog> list = dogMap.get(10);
    loop(list);
  }

  @Benchmark
  @OperationsPerInvocation(100)
  public void measureWrong_100() {
    List<Dog> list = dogMap.get(100);
    loop(list);
  }

  @Benchmark
  @OperationsPerInvocation(1000)
  public void measureWrong_1000() {
    List<Dog> list = dogMap.get(1000);
    loop(list);
  }

  @Benchmark
  @OperationsPerInvocation(10000)
  public void measureWrong_10000() {
    List<Dog> list = dogMap.get(10000);
    loop(list);
  }

  @Benchmark
  @OperationsPerInvocation(100000)
  public void measureWrong_100000() {
    List<Dog> list = dogMap.get(100000);
    loop(list);
  }
  
  @Benchmark
  @OperationsPerInvocation(1000000)
  public void measureWrong_1000000() {
    List<Dog> list = dogMap.get(1000000);
    loop(list);
  }

  /*
   * ============================== HOW TO RUN THIS TEST:
   * ====================================
   *
   * You might notice the larger the repetitions count, the lower the
   * "perceived" cost of the operation being measured. Up to the point we do
   * each addition with 1/20 ns, well beyond what hardware can actually do.
   *
   * This happens because the loop is heavily unrolled/pipelined, and the
   * operation to be measured is hoisted from the loop. Morale: don't overuse
   * loops, rely on JMH to get the measurement right.
   *
   * You can run this test:
   *
   * a) Via the command line: $ mvn clean install $ java -jar
   * target/benchmarks.jar JMHSample_11 -wi 5 -i 5 -f 1 (we requested 5
   * warmup/measurement iterations, single fork)
   *
   * b) Via the Java API: (see the JMH homepage for possible caveats when
   * running from IDE: http://openjdk.java.net/projects/code-tools/jmh/)
   */

  public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
        .include(J8Loop.class.getSimpleName())
        .warmupIterations(2)
        .measurementIterations(2).forks(1).build();

    new Runner(opt).run();
  }

}
class Dog{
  private int age;
  private String name;
  public Dog(int age, String name) {
    super();
    this.age = age;
    this.name = name;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  @Override
  public String toString() {
    return "Dog [age=" + age + ", name=" + name + "]";
  }
}
View Code

相关文章: