7.FixtureLevelInvocation (固定级别调用)
package org.openjdk.jmh.samples; 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.concurrent.*; @OutputTimeUnit(TimeUnit.MICROSECONDS) // 报告时间单位 @BenchmarkMode(Mode.AverageTime) // 输出报告 @Warmup(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //预热次数和时间 @Measurement(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //测试次数和时间 public class JMHSample_07_FixtureLevelInvocation { @State(Scope.Benchmark) // 共享一个对象 public static class NormalState { ExecutorService service; @Setup(Level.Trial) // 整个基准调用之前进行准备工作 (全局一次) public void up() { System.out.println("--------准备工作"); service = Executors.newCachedThreadPool(); } @TearDown(Level.Trial) // 整个基准结束之后进行检查工作 (全局一次) public void down() { System.out.println("------------销毁"); service.shutdown(); } } public static class LaggingState extends NormalState { public static final int SLEEP_TIME = Integer.getInteger("sleepTime", 10); @Setup(Level.Invocation) //每次方法被调用都会执行 public void lag() throws InterruptedException { TimeUnit.MILLISECONDS.sleep(SLEEP_TIME); //调用的时候会睡眠10毫秒 } } @Benchmark @BenchmarkMode(Mode.AverageTime) // 输出的方式是每次多少秒 public double measureHot(NormalState e, final Scratch s) throws ExecutionException, InterruptedException { return e.service.submit(new Task(s)).get(); } @Benchmark @BenchmarkMode(Mode.AverageTime) //输出的方式是每次多少秒 public double measureCold(LaggingState e, final Scratch s) throws ExecutionException, InterruptedException { return e.service.submit(new Task(s)).get(); } @State(Scope.Thread) // 独享下面的静态方法 public static class Scratch { private double p; public double doWork() { p = Math.log(p); return p; } } public static class Task implements Callable<Double> { private Scratch s; public Task(Scratch s) { this.s = s; } @Override public Double call() { return s.doWork(); } } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(JMHSample_07_FixtureLevelInvocation.class.getSimpleName()) .forks(1) .build(); new Runner(opt).run(); } }
8.DeadCode (JVM调优)
源码
package org.openjdk.jmh.samples; 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.concurrent.TimeUnit; /*** 这个列子展示了一种场景: 有些代码会变成JVM优化掉,使得基准测试结果不可用。 * * baseline(): 空方法 * * measureWrong(): 由于计算结果并没有返回,JVM会自动优化,使其耗时测得与baseline()结果一样 * * measureRight(): 将计算结果返回,JVM自动优化,这样才能真实测得真实的对象 * */ @State(Scope.Thread) //静态内部可省略 @BenchmarkMode(Mode.AverageTime) //每次多少秒 @OutputTimeUnit(TimeUnit.NANOSECONDS) //单位 @Warmup(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //预热次数和时间 @Measurement(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //测试次数和时间 public class JMHSample_08_DeadCode { private double x = Math.PI; private double compute(double d) { for (int c = 0; c < 10; c++) { d = d * d / Math.PI; } return d; } @Benchmark public void baseline() { // do nothing, this is a baseline } @Benchmark public void measureWrong() { // This is wrong: result is not used and the entire computation is optimized away. compute(x); //因为我们没有使用计算结果,JVM会自动把这段代码优化掉,相当于测试了一个空方法。 } @Benchmark public double measureRight() { // This is correct: the result is being used. return compute(x); //让JVM不能优化掉,我们返回了结果 } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(JMHSample_08_DeadCode.class.getSimpleName()) .forks(1) .jvmArgs("-server") //注意这里一定要设置server模式,为了充分使用JVM调优。 .build(); new Runner(opt).run(); } }
9.Blackholes (黑洞拒绝JVM调优)
黑洞: 可以有效的避免过于激进的优化
源码
@Benchmark public void measureRight_2(Blackhole bh) { // 如果执行结果不使用编译器优化 // 为了防止编译器自作主张,这里使用JMH提供的黑洞对象执行结果进行消费 bh.consume(compute(x1)); bh.consume(compute(x2)); }
package org.openjdk.jmh.samples; 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.concurrent.TimeUnit; /** * 空方法会被JVM给优化掉,但黑洞可以拒绝优化 */ @BenchmarkMode(Mode.AverageTime) //输出方式 @OutputTimeUnit(TimeUnit.NANOSECONDS) //输出的时间 @State(Scope.Thread) //面内部静态 @Warmup(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //预热次数和时间 @Measurement(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //测试次数和时间 public class JMHSample_09_Blackholes { double x1 = Math.PI; double x2 = Math.PI * 2; private double compute(double d) { for (int c = 0; c < 10; c++) { d = d * d / Math.PI; } return d; } @Benchmark public double baseline() { //整一个,且真实有效的只有一个 return compute(x1); } @Benchmark public double measureWrong() { //整两个,但真正有效值为1个 compute(x1); //编译器自动识别,直接被JVM优化掉 return compute(x2); } @Benchmark public double measureRight_1() { //整两个且真实计算的有2个 return compute(x1) + compute(x2); } @Benchmark public void measureRight_2(Blackhole bh) { // 如果执行结果不使用编译器优化 // 为了防止编译器自作主张,这里使用JMH提供的黑洞对象执行结果进行消费 bh.consume(compute(x1)); bh.consume(compute(x2)); } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(JMHSample_09_Blackholes.class.getSimpleName()) .forks(1) .build(); new Runner(opt).run(); } }