下面给出一个基准测试的例子, 创建50个线程去并发执行一个方法

  1. public static void main(String[] args) throws RunnerException {
  2. Options opt = new OptionsBuilder()
  3. .include(Sample3_States.class.getSimpleName())
  4. .threads(50)
  5. .forks(1)
  6. .build();
  7. new Runner(opt).run();
  8. }

1. 整个Benchmark共用一个入参

直接看完整代码

  1. @Warmup(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS)
  2. @Measurement(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS)
  3. public class Sample3_States {
  4. //这个静态内部类会在整个Benchmark启动的时候初始化, 作为入参
  5. //所有测试线程共享一个实例, 用于测试有状态实例在多线程共享下的性能
  6. //一般用来测试多线程竞争下的性能
  7. @State(Scope.Benchmark)//整个测试总共用这一个对象
  8. public static class BenchmarkState {
  9. volatile double x = Math.PI;
  10. }
  11. //这个依然启动4个线程, 但是入参是同一个实例, 竞争会非常激烈
  12. @Benchmark
  13. public void measureShared(BenchmarkState state) {//此次基准测试所有的线程入参都是同一个对象
  14. state.x++;
  15. }
  16. public static void main(String[] args) throws RunnerException {
  17. Options opt = new OptionsBuilder()
  18. .include(Sample3_States.class.getSimpleName())
  19. .threads(50)
  20. .forks(1)
  21. .build();
  22. new Runner(opt).run();
  23. }
  24. }

执行结果

  1. Benchmark Mode Cnt Score Error Units
  2. Sample3_States.measureShared thrpt 40765443.353 ops/s

这个数字是 4000万
此Benchmark创建了50个线程一起执行这个方法, 互相会有线程竞争这个 volatile 变量导致效率降低


2. 线程专属入参

直接看完整代码

  1. @Warmup(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS)
  2. @Measurement(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS)
  3. public class Sample3_States {
  4. //这个静态内部类会在Benchmark 各个线程执行之前 初始化, 作为入参
  5. //所有测试线程各用各的
  6. @State(Scope.Thread)//不同线程不同的对象
  7. public static class ThreadState {
  8. volatile double x = Math.PI;
  9. }
  10. //根据main方法, 会启动4个线程去一起执行
  11. //每个线程的入参都是不同的
  12. @Benchmark
  13. public void measureUnshared(ThreadState state) { //每个线程的入参都不同
  14. state.x++;
  15. }
  16. public static void main(String[] args) throws RunnerException {
  17. Options opt = new OptionsBuilder()
  18. .include(Sample3_States.class.getSimpleName())
  19. .threads(50)
  20. .forks(1)
  21. .build();
  22. new Runner(opt).run();
  23. }
  24. }

执行结果:

  1. Benchmark Mode Cnt Score Error Units
  2. Sample3_States.measureUnshared thrpt 1670970565.887 ops/s

这个数字是16亿
由于这个变量是线程独享的, 不存在竞争的情况, 所以执行效率会非常高, 直接提升了40倍左右的效率


@State还有一种线程组的作用域 @State(Scope._Group_), 这里就不详细介绍了, 因为使用场景实在太少, 既影响可读性, 测试结果也模棱两可完全违背基准测试的目的


3. 入参初始化@Setup

通过使用@Setup注解可以在基准测试的不同生命周期 调用指定方法

  1. @Warmup(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS)
  2. @Measurement(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS)
  3. public class Sample3_States {
  4. @State(Scope.Benchmark)//整个测试总共用这一个对象
  5. public static class BenchmarkState {
  6. volatile double x;
  7. //@Setup(Level.Trial) //整个基准测试之前只会被调用一次
  8. //@Setup(Level.Iteration) //每轮基准测试之前都会被调用一次
  9. @Setup(Level.Invocation) //每次基准测试的方法被调用之前,都会被调用一次
  10. public void setup(){
  11. System.out.println("初始化");
  12. x = Math.PI;
  13. }
  14. }
  15. @Benchmark
  16. public void measureShared(BenchmarkState state) {
  17. state.x++;
  18. }
  19. public static void main(String[] args) throws RunnerException {
  20. Options opt = new OptionsBuilder()
  21. .include(Sample3_States.class.getSimpleName())
  22. .threads(50)
  23. .forks(1)
  24. .build();
  25. new Runner(opt).run();
  26. }
  27. }
  • @Setup(Level.Trial) //整个基准测试只会被调用一次
  • @Setup(Level.Iteration) //每轮基准测试都会被调用一次
  • @Setup(Level.Invocation) //每次基准测试的方法被调用前,都会被调用一次

4. 销毁入参@TearDown

虽然叫”销毁”, 但不一定非要做销毁的操作

  1. @Warmup(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS)
  2. @Measurement(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS)
  3. public class Sample3_States {
  4. @State(Scope.Benchmark)//整个测试总共用这一个对象
  5. public static class BenchmarkState {
  6. volatile double x;
  7. //@TearDown(Level.Trial) //整个基准测试之后,只会被调用一次
  8. //@TearDown(Level.Iteration) //每轮基准测试之后,都会被调用一次
  9. @TearDown(Level.Invocation) //每次基准测试的方法被调用之后,都会被调用一次
  10. public void tearDown(){
  11. System.out.println("销毁操作");
  12. assert x > Math.PI : "Nothing changed?"; //一般可以在tearDown里面做一些断言操作
  13. }
  14. }
  15. @Benchmark
  16. public void measureShared(BenchmarkState state) {
  17. state.x++;
  18. }
  19. public static void main(String[] args) throws RunnerException {
  20. Options opt = new OptionsBuilder()
  21. .include(Sample3_States.class.getSimpleName())
  22. .threads(50)
  23. .forks(1)
  24. .jvmArgs("-ea") //开启断言检测;assertion在一般情况下是关闭的,通过java -ea 可以打开该功能,关闭为 -da
  25. .build();
  26. new Runner(opt).run();
  27. }
  28. }