写点什么

性能测试中唯一标识的 JMH 测试

作者:FunTester
  • 2024-04-15
    河北
  • 本文字数:2646 字

    阅读完需:约 9 分钟

前文分享了几种性能测试中常用到的生成全局唯一标识的案例,虽然在文中我猜测了几种方案设计的性能,并根据自己的经验给出了适用的场景。


但对于一个性能测试工程师来讲,有真是测试数据才更有说服力。这让我想起来之前学过的 Java 微基准测试框架 JMH ,所以不妨一试。

JMH 简介

JMH (Java Microbenchmark Harness)是一个用于编写和运行 Java 基准测试的工具。它被广泛用于评估 Java 应用程序的性能,并帮助开发人员发现和优化性能瓶颈。


JMH 的主要特点包括:


  1. 高可信度:JMH 提供了多种机制来消除测试过程中的噪音和偏差,确保测试结果的可靠性。

  2. 易用性:JMH 提供了丰富的注解和 API,使编写和运行基准测试变得相对简单。

  3. 灵活性:JMH 支持多种测试模式,如简单的吞吐量测试、微基准测试以及更复杂的测试场景。

  4. 可扩展性:JMH 允许用户自定义测试环境,如 GC 策略、编译器选项等,以满足特定的性能评估需求。

  5. 广泛应用:JMH 被广泛应用于 Java 生态系统中,包括 JDK 自身的性能优化、第三方开源库的性能评估等。


JMH 是 Java 开发者评估应用程序性能的强大工具,有助于提高 Java 应用程序的整体质量和性能。同样地对于性能测试而言,也可以通过 JMH 测试评估一段代码在实际执行当中的表现。

实测

除了 使用分布式服务生成GUID 这个方案以外,其他四种方案(其中两种是我自己常用的)均参与测试。原因是分布式服务需要网络交互,这个一听就不高性能,还有我暂时没条件测试这个。


下面有限展示实测结果,总结使用线程共享和线程独享的方案性能均远远高于 UUID雪花算法 。为了省事儿以下测试均预热 2 次,预热批次大小 2,测试迭代次数 1 次,迭代批次大小也是 1 次。配置如下:


                .warmupIterations(2)//预热次数                .warmupBatchSize(2)//预热批次大小                .measurementIterations(1)//测试迭代次数                .measurementBatchSize(1)//测试批次大小                .build();
复制代码


PS:JMH 貌似还不支持 Groovy 所以我用 Java 写了这个用例。


下面是运行 1 个线程的测试结果:


UniqueNumberTest.exclusive  thrpt       203.146          ops/usUniqueNumberTest.share      thrpt        99.860          ops/usUniqueNumberTest.snow       thrpt         4.096          ops/usUniqueNumberTest.uuid       thrpt        11.758          ops/us
复制代码


下面是运行 10 个线程的测试结果:


Benchmark                    Mode  Cnt     Score   Error   UnitsUniqueNumberTest.exclusive  thrpt       1117.347          ops/usUniqueNumberTest.share      thrpt        670.141          ops/usUniqueNumberTest.snow       thrpt         10.925          ops/usUniqueNumberTest.uuid       thrpt          3.608          ops/us
复制代码


PS:此时机器的性能基本跑满了。


下面是 40 个线程的测试结果:


Benchmark                    Mode  Cnt     Score   Error   UnitsUniqueNumberTest.exclusive  thrpt       1110.273          ops/usUniqueNumberTest.share      thrpt        649.350          ops/usUniqueNumberTest.snow       thrpt          8.908          ops/usUniqueNumberTest.uuid       thrpt          4.205          ops/us
复制代码


可以看出跟 10 个线程结果差不多。


本机配置 12 核心,以上的测试结果单位是微秒,把结果乘以 100 万就是每秒的处理量,各位在使用不同方案时可以适当参考。

测试用例

下面是我的测试用例,测试结果我就不进行可视化了。


package com.funtest.jmh;    import com.funtester.utils.SnowflakeUtils;  import org.openjdk.jmh.annotations.*;  import org.openjdk.jmh.infra.Blackhole;  import org.openjdk.jmh.results.format.ResultFormatType;  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.UUID;  import java.util.concurrent.TimeUnit;  import java.util.concurrent.atomic.AtomicInteger;    @BenchmarkMode(Mode.Throughput)  //@Warmup(Ω = 3, time = 2, timeUnit = TimeUnit.SECONDS)//预热次数,含义是每个测试会跑多久  //@Measurement(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)//测试迭代次数,含义是每个测试会跑多久  //@Threads(1)//测试线程数  //@Fork(2)//fork表示每个测试会fork出几个进程,也就是说每个测试会跑几次  @State(value = Scope.Thread)//默认为Scope.Thread,含义是每个线程都会有一个实例  @OutputTimeUnit(TimeUnit.MICROSECONDS)  public class UniqueNumberTest {        SnowflakeUtils snowflakeUtils = new SnowflakeUtils(1, 1);        ThreadLocal<Integer> exclusive = ThreadLocal.withInitial(() -> 0);        AtomicInteger share = new AtomicInteger(0);        @Benchmark      public void uuid() {          UUID.randomUUID();      }        @Benchmark      public void snow() {          snowflakeUtils.nextId();      }        @Benchmark      public void exclusive(Blackhole blackhole) {          Integer i = exclusive.get();          i++;          blackhole.consume(i + "");      }        @Benchmark      public void share(Blackhole blackhole) {          blackhole.consume(share.incrementAndGet() + "");      }        public static void main(String[] args) throws RunnerException {          Options options = new OptionsBuilder()                  .include(UniqueNumberTest.class.getSimpleName())//测试类名                  .result("long/result.json")//测试结果输出到result.json文件                  .resultFormat(ResultFormatType.JSON)//输出格式                  .forks(1)//fork表示每个测试会fork出几个进程,也就是说每个测试会跑几次                  .threads(40)//测试线程数                  .warmupIterations(2)//预热次数                  .warmupBatchSize(2)//预热批次大小                  .measurementIterations(1)//测试迭代次数                  .measurementBatchSize(1)//测试批次大小                  .build();          new Runner(options).run();      }      }
复制代码

发布于: 刚刚阅读数: 4
用户头像

FunTester

关注

公众号:FunTester,800篇原创,欢迎关注 2020-10-20 加入

Fun·BUG挖掘机·性能征服者·头顶锅盖·Tester

评论

发布
暂无评论
性能测试中唯一标识的JMH测试_FunTester_InfoQ写作社区