3.@State (状态)【类】
源码
@State(Scope.Thread) //各个线程独自占有一个对象
@State(Scope.Benchmark) //整个测试总共用这一个对象
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; /** * @State(Scope.XXXX) 描述了这个类对象的作用域 * 测试共享与独享的区别 */ @Warmup(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //预热次数和时间 @Measurement(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //测试次数和时间 public class JMHSample_03_States { // 所有测试线程各用各的 (独享) @State(Scope.Thread) //各个线程独自占有一个对象 public static class ThreadState { //在我们执行Benchmark的时候,他会生成一个ThreadState对象。可以被Benchmark直接使用的,也就是入参。 volatile double x = Math.PI; } // 根据main方法,会启动4个线程去一起执行 @Benchmark public void measureUnshared(ThreadState state) { //每一个线程的入参都是不同的对象 state.x++; } // 所有测试共享一个实列,用于测试有状态实列在多线程共享下的性能 // 一般用来测试多线程竞争下的性能 @State(Scope.Benchmark) //整个测试总共用这一个对象 public static class BenchmarkState { volatile double x = Math.PI; } @Benchmark public void measureShared(BenchmarkState state) { //这个依然启动4个进程,但是入参都是同一个实列,竞争非常激烈 state.x++; } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(JMHSample_03_States.class.getSimpleName()) .threads(4) //在执行Benchmark的时候会启动4个线程 .forks(1) .build(); new Runner(opt).run(); } }
4.DefaultState (默认状态)【类】
源码
@State(Scope.Thread) //放在整体类上
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; @State(Scope.Thread) //我们标记这个类会成为一个对象由JMH进行管理。多个线程会创建多个下面类对象 @Warmup(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //预热次数和时间 @Measurement(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //测试次数和时间 public class JMHSample_04_DefaultState { double x = Math.PI; // 我们使用默认静态类的主要目的是: 我们不用特地的去写一个静态内部类去声明一个入参。如果没有上面的注解那么我们就需要声明一个静态内部类 @Benchmark public void measure() { x++; } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(JMHSample_04_DefaultState.class.getSimpleName()) .forks(1) .build(); new Runner(opt).run(); } }
5.@Setup和@TearDown (初始化和销毁)【方法】
启动之前准备工作
// 启动Benchmark之前的准备工作 @Setup // 必须在@State下的类中才能使用,实际上也算是@state管理对象的生命周期一部分
结束之后检查工作
// Benchmark结束之后的检查工作 @TearDown //必须在@State下的类中才能使用
源码
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; /** * 控制state 对象中的方法在什么时候执行 * * @Setup state中的方法如何被执行 * @TearDown state中的方法如何执行 * level.Trial 默认的执行策略,整个基准测试执行一次 */ @State(Scope.Thread) //生命默认静态类,我们可以不用进行手动传入参数 @Warmup(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //预热次数和时间 @Measurement(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //测试次数和时间 public class JMHSample_05_StateFixtures { double x; // 启动Benchmark之前的准备工作 @Setup // 必须在@State下的类中才能使用,实际上也算是@state管理对象的生命周期一部分 public void prepare() { x = Math.PI; System.out.println("--------------1"); } // Benchmark结束之后的检查工作 @TearDown //必须在@State下的类中才能使用 public void check() { System.out.println("--------------2"); // 这里使用了断言 assert x > Math.PI : "Nothing changed?"; } @Benchmark public void measureRight() { //正确代码,正常执行 x++; } @Benchmark public void measureWrong() { // 这里是错误代码实列,会在Benchmark执行完毕后报错 double x = 0; x++; } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(JMHSample_05_StateFixtures.class.getSimpleName()) .forks(1) .jvmArgs("-ea") //开启断言检测: assertion在一般情况下是关闭的,通过 java -ea 可以打开改功能,关闭为 -da .build(); new Runner(opt).run(); } }
6.FixtureLevel (初始化和销毁等级)【方法】
源码
@Setup(Level.Trial) // 与TearDown同理.启动之前会执行一次 @TearDown(Level.Trial) // 整个完整的基准测试之后才会执行一次 @TearDown(Level.Iteration) // 每轮循环完成之后才会执行一次 @TearDown(Level.Invocation) // 每次方法被调用都会执行一次
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; @State(Scope.Thread) //可以免除静态内部类 @Warmup(iterations = 1,time = 1,timeUnit = TimeUnit.SECONDS) //预热次数和时间 @Measurement(iterations = 3,time = 1,timeUnit = TimeUnit.SECONDS) //测试次数和时间 public class JMHSample_06_FixtureLevel { double x; // @Setup(Level.Trial) // 与TearDown同理.启动之前会执行一次 // @TearDown(Level.Trial) // 整个完整的基准测试之后才会执行一次 // @TearDown(Level.Iteration) // 每轮循环完成之后才会执行一次 // @TearDown(Level.Invocation) // 每次方法被调用都会执行一次 @TearDown(Level.Iteration) // 每次方法被调用都会执行一次 public void check() { System.out.println("----------1"); assert x > Math.PI : "Nothing changed?"; } @Benchmark public void measureRight() { x++; } @Benchmark public void measureWrong() { double x = 0; x++; } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(JMHSample_06_FixtureLevel.class.getSimpleName()) .forks(1) .jvmArgs("-ea") .shouldFailOnError(false) // 默认是false,即使assert错误也不会让整个测试失败 .build(); new Runner(opt).run(); } }