news 2026/4/10 12:37:39

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
性能测试中唯一标识的JMH测试

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

但对于一个性能测试工程师来讲,有真是测试数据才更有说服力。这让我想起来之前学过的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次。配置如下:

  1. .warmupIterations(2)//预热次数

  2. .warmupBatchSize(2)//预热批次大小

  3. .measurementIterations(1)//测试迭代次数

  4. .measurementBatchSize(1)//测试批次大小

  5. .build();

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

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

  1. UniqueNumberTest.exclusive thrpt 203.146 ops/us

  2. UniqueNumberTest.share thrpt 99.860 ops/us

  3. UniqueNumberTest.snow thrpt 4.096 ops/us

  4. UniqueNumberTest.uuid thrpt 11.758 ops/us

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

  1. Benchmark Mode Cnt Score Error Units

  2. UniqueNumberTest.exclusive thrpt 1117.347 ops/us

  3. UniqueNumberTest.share thrpt 670.141 ops/us

  4. UniqueNumberTest.snow thrpt 10.925 ops/us

  5. UniqueNumberTest.uuid thrpt 3.608 ops/us

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

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

  1. Benchmark Mode Cnt Score Error Units

  2. UniqueNumberTest.exclusive thrpt 1110.273 ops/us

  3. UniqueNumberTest.share thrpt 649.350 ops/us

  4. UniqueNumberTest.snow thrpt 8.908 ops/us

  5. UniqueNumberTest.uuid thrpt 4.205 ops/us

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

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

测试用例

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

  1. package com.funtest.jmh;

  2. import com.funtester.utils.SnowflakeUtils;

  3. import org.openjdk.jmh.annotations.*;

  4. import org.openjdk.jmh.infra.Blackhole;

  5. import org.openjdk.jmh.results.format.ResultFormatType;

  6. import org.openjdk.jmh.runner.Runner;

  7. import org.openjdk.jmh.runner.RunnerException;

  8. import org.openjdk.jmh.runner.options.Options;

  9. import org.openjdk.jmh.runner.options.OptionsBuilder;

  10. import java.util.UUID;

  11. import java.util.concurrent.TimeUnit;

  12. import java.util.concurrent.atomic.AtomicInteger;

  13. @BenchmarkMode(Mode.Throughput)

  14. //@Warmup(Ω = 3, time = 2, timeUnit = TimeUnit.SECONDS)//预热次数,含义是每个测试会跑多久

  15. //@Measurement(iterations = 3, time = 5, timeUnit = TimeUnit.SECONDS)//测试迭代次数,含义是每个测试会跑多久

  16. //@Threads(1)//测试线程数

  17. //@Fork(2)//fork表示每个测试会fork出几个进程,也就是说每个测试会跑几次

  18. @State(value = Scope.Thread)//默认为Scope.Thread,含义是每个线程都会有一个实例

  19. @OutputTimeUnit(TimeUnit.MICROSECONDS)

  20. public class UniqueNumberTest {

  21. SnowflakeUtils snowflakeUtils = new SnowflakeUtils(1, 1);

  22. ThreadLocal<Integer> exclusive = ThreadLocal.withInitial(() -> 0);

  23. AtomicInteger share = new AtomicInteger(0);

  24. @Benchmark

  25. public void uuid() {

  26. UUID.randomUUID();

  27. }

  28. @Benchmark

  29. public void snow() {

  30. snowflakeUtils.nextId();

  31. }

  32. @Benchmark

  33. public void exclusive(Blackhole blackhole) {

  34. Integer i = exclusive.get();

  35. i++;

  36. blackhole.consume(i + "");

  37. }

  38. @Benchmark

  39. public void share(Blackhole blackhole) {

  40. blackhole.consume(share.incrementAndGet() + "");

  41. }

  42. public static void main(String[] args) throws RunnerException {

  43. Options options = new OptionsBuilder()

  44. .include(UniqueNumberTest.class.getSimpleName())//测试类名

  45. .result("long/result.json")//测试结果输出到result.json文件

  46. .resultFormat(ResultFormatType.JSON)//输出格式

  47. .forks(1)//fork表示每个测试会fork出几个进程,也就是说每个测试会跑几次

  48. .threads(40)//测试线程数

  49. .warmupIterations(2)//预热次数

  50. .warmupBatchSize(2)//预热批次大小

  51. .measurementIterations(1)//测试迭代次数

  52. .measurementBatchSize(1)//测试批次大小

  53. .build();

  54. new Runner(options).run();

  55. }

  56. }

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/7 7:00:42

Dify部署过程中遇到Qwen3-VL-8B加载失败的解决方案

Dify 部署 Qwen3-VL-8B 加载失败&#xff1f;一文讲透根源与实战修复 在构建智能客服系统时&#xff0c;客户拍了一张产品照片发来&#xff1a;“这包是正品吗&#xff1f;”——如果 AI 能“看懂”这张图并回答“这是 LV 的 Neverfull 手袋&#xff0c;但拉链细节疑似仿品”&a…

作者头像 李华
网站建设 2026/4/9 7:57:04

MySQL深度优化(3):查询语句改写技巧

你敢信吗&#xff1f;⼀个政务系统的分⻚查询从5秒优化到0.1秒&#xff0c;只改了3⾏SQL&#xff01;上周有个学员分享他们的案例&#xff1a;公安⼾籍查询系统&#xff0c;查询第1000⻚数据时&#xff0c;LIMIT 99900, 100耗时5.2秒&#xff0c;⽤⼾投诉不断。后来我们⽤了3个…

作者头像 李华
网站建设 2026/4/8 4:54:26

15、JSTL 国际化与本地化开发指南

JSTL 国际化与本地化开发指南 在当今全球化的互联网环境中,开发支持多语言和多地区的 Web 应用程序变得越来越重要。JSTL(JavaServer Pages Standard Tag Library)提供了一系列强大的工具,用于实现 Web 应用的国际化(I18N)和本地化(L10N)。本文将深入探讨 JSTL 中与国…

作者头像 李华
网站建设 2026/4/5 9:05:26

17、JSTL格式化操作:数字、日期与货币的本地化处理

JSTL格式化操作:数字、日期与货币的本地化处理 在当今全球化的互联网环境中,让网站能够被尽可能多的人访问至关重要。除了文本本地化,数字、日期和货币的本地化同样不可忽视。例如,日期“06/12/2004”,在美国人看来是6月12日,而大多数欧洲人会认为是12月6日。幸运的是,…

作者头像 李华
网站建设 2026/4/3 2:48:39

20、JSTL 创建数据源全解析

JSTL 创建数据源全解析 在开发 Web 应用时,创建数据源是与数据库交互的重要步骤。本文将详细介绍使用 JSTL 创建数据源的三种主要方法,帮助你根据不同的需求选择合适的方式。 1. 创建数据源的三种基本方式 从根本上来说,有三种创建数据源的方式,具体如下表所示: | 创建…

作者头像 李华
网站建设 2026/4/1 18:18:41

25、JSTL XML处理及常用动作参考详解

JSTL XML处理及常用动作参考详解 1. XML过滤 在处理XML文档时,可以使用SAX(Simple API for XML)过滤器来过滤特定的元素。SAX 是一种独立于语言、基于事件的 XML 解析 API,它通过回调方法来报告解析事件,如元素的开始和结束等。 例如,对于以下简单的 XML 文档: <…

作者头像 李华