项目作者: blinkfox

项目描述 :
A small library for performance evaluation of Java code. 这是一个用来对Java代码做性能评估的工具库。
高级语言: Java
项目地址: git://github.com/blinkfox/stalker.git
创建时间: 2019-02-01T15:35:33Z
项目社区:https://github.com/blinkfox/stalker

开源协议:Apache License 2.0

下载


stalker

Hits Build Status GitHub license codecov Java Version

这是一个简单、轻量级和可编程的 Java 代码性能测试工具库。

一、特性

  • 简单、轻量级(jar 包仅52kb)和可编程的性能测试工具库
  • 支持对性能的多种统计纬度
  • API 简单易用,易于集成或扩展

二、快速集成

1. Maven

  1. <dependency>
  2. <groupId>com.blinkfox</groupId>
  3. <artifactId>stalker</artifactId>
  4. <version>1.2.3</version>
  5. </dependency>

2. Gradle

  1. compile 'com.blinkfox:stalker:1.2.3'

三、API 介绍和使用

预先准备

在对Java方法做性能测试之前,先准备好待测试的类和方法:

  1. /**
  2. * 用于测量(仅测试使用)该类中的方法的执行耗时的类.
  3. *
  4. * @author blinkfox on 2019-02-03.
  5. */
  6. @Slf4j
  7. public class MyTestService {
  8. /**
  9. * 测试方法1,模拟业务代码耗时 2~5 ms,且会有约 1% 的几率执行异常.
  10. */
  11. public void hello() {
  12. // 模拟运行时抛出异常.
  13. if (new Random().nextInt(100) == 5) {
  14. throw new MyServiceException("My Service Exception.");
  15. }
  16. // 模拟运行占用约 2~5 ms 的时间.
  17. this.sleep(2L + new Random().nextInt(3));
  18. }
  19. /**
  20. * 测试方法2,模拟业务代码运行占用约 2 ms 的时间.
  21. */
  22. public void fastHello() {
  23. this.sleep(2L);
  24. }
  25. /**
  26. * 本线程调用该方法时,睡眠指定时间,用来模拟业务耗时.
  27. *
  28. * @param time 时间
  29. */
  30. private void sleep(long time) {
  31. try {
  32. Thread.sleep(time);
  33. } catch (InterruptedException e) {
  34. log.info("InterruptedException", e);
  35. Thread.currentThread().interrupt();
  36. }
  37. }
  38. }

Stalker 类

1. 最简示例

以下代码将会预热5次,然后在单线程下正式执行10次,从而将运行结果计算统计并输出出来:

  1. public static void main(String[] args) {
  2. Stalker.run(() -> new MyTestService().hello());
  3. }

以上结果将默认在控制台输出:

  1. +------------------------------------------------------------------------------------------------------------------------------------------------------+
  2. | threads: 1, concurrens: 1, warmups:5, runs: 10, printErrorLog: false |
  3. +---+----------+-------+---------+---------+------------+----------+---------+---------+---------+---------+---------------------+---------------------+
  4. | | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence |
  5. +---+----------+-------+---------+---------+------------+----------+---------+---------+---------+---------+---------------------+---------------------+
  6. | 1 | 40.52 ms | 10 | 10 | 0 | 246.76 | 40.43 ms | 4.04 ms | 2.24 ms | 7.74 ms | 1.56 ms | 3.07 ms | 5.01 ms |
  7. +---+----------+-------+---------+---------+------------+----------+---------+---------+---------+---------+---------------------+---------------------+

也可以获取到统计结果:

  1. // 获取运行的统计结果.
  2. MeasureResult[] measureResults = mStalker.runStatis(() -> new MyTestService().hello());
  3. // 获取运行的 Options 中指定的 MeasureOutput 结果,默认是控制台中输出的 ASCII 表格的字符串内容,
  4. // 你可以实现 MeasureOutput 接口,来实现自定义的结果返回,可以是多个结果,所以返回集合.
  5. List<Object> measurements = mStalker.run(() -> new MyTestService().hello());

2. 更全示例

以下代码表示,两个方法hello()fastHello()将会预热10次,在1000个线程200个并发下,每次执行5次:

  1. Stalker.run(Options.of(1000, 200).warmups(10).runs(5),
  2. () -> new MyTestService().hello(),
  3. () -> new MyTestService().fastHello());

以上结果将默认在控制台输出:

  1. +-------------------------------------------------------------------------------------------------------------------------------------------------------+
  2. | threads: 1000, concurrens: 200, warmups:10, runs: 5, printErrorLog: false |
  3. +---+-----------+-------+---------+---------+------------+---------+---------+---------+----------+---------+---------------------+---------------------+
  4. | | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence |
  5. +---+-----------+-------+---------+---------+------------+---------+---------+---------+----------+---------+---------------------+---------------------+
  6. | 1 | 668.93 ms | 5000 | 4955 | 45 | 7474.57 | 17.22 s | 3.48 ms | 2.01 ms | 11.66 ms | 1.14 ms | 3.44 ms | 3.51 ms |
  7. | 2 | 348.85 ms | 5000 | 5000 | 0 | 14332.69 | 11.23 s | 2.25 ms | 2.01 ms | 3.32 ms | 0.19 ms | 2.24 ms | 2.25 ms |
  8. +---+-----------+-------+---------+---------+------------+---------+---------+---------+----------+---------+---------------------+---------------------+

结果说明:

  • Costs: 实际正式运行所消耗的总时间
  • Total: 正式运行的总次数
  • Success: 正式运行的成功次数
  • Failure: 正式运行的失败次数
  • Throughput: 正式运行的吞吐量
  • Sum: 每次运行的耗时结果求和之后的值
  • Avg: 所有运行耗时结果的算术平均数
  • Min: 所有运行耗时结果中最小值
  • Max: 所有运行耗时结果中最大值
  • StdDev: 所有运行耗时结果的标准方差
  • 95% LowerConfidence: 95%置信区间的最小边界值
  • 95% LowerConfidence: 95%置信区间的最大边界值

3. submit 异步执行

Stalker 中的 run 方法默认是同步执行的,如果你的性能测试任务耗时比较久,可以直接调用 submit 来异步提交性能测试任务,submit 将返回 StalkerFuture 的实例,后续你就可以通过 StalkerFuture 实时获取任务执行情况或取消执行中的任务等。

下面是在 20 个线程、5 并发下的异步执行情况:

  1. @Test
  2. public void submitWithSlowMethod() {
  3. StalkerFuture stalkerFuture = Stalker
  4. .submit(Options.of("SlowTest", 20, 5), () -> new MyTestService().slowHello())
  5. .waitDone(future -> {
  6. Assert.assertNotNull(future.getFirst());
  7. }, 50L)
  8. .done(future -> {
  9. log.info("任务已完成,获取最后的执行结果.");
  10. future.get();
  11. });
  12. Assert.assertTrue(stalkerFuture.isDone());
  13. }

执行将得到如下类似结果:

  1. +-------------------------------------------------------------------------------------------------------------------------------------+
  2. | duration: 2 s, concurrens: 4, warmups:5, runs: 1, printErrorLog: false |
  3. +---+-------+-------+---------+---------+------------+------+------+------+------+--------+---------------------+---------------------+
  4. | | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence |
  5. +---+-------+-------+---------+---------+------------+------+------+------+------+--------+---------------------+---------------------+
  6. | 1 | 0 ns | 0 | 0 | 0 | 0.00 | 0 ns | 0 ns | 0 ns | 0 ns | 0 ns | 0 ns | 0 ns |
  7. +---+-------+-------+---------+---------+------------+------+------+------+------+--------+---------------------+---------------------+
  8. +---------------------------------------------------------------------------------------------------------------------------------------------------+
  9. | duration: 2 s, concurrens: 4, warmups:5, runs: 1, printErrorLog: false |
  10. +---+--------+-------+---------+---------+------------+--------+----------+------+-----------+----------+---------------------+---------------------+
  11. | | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence |
  12. +---+--------+-------+---------+---------+------------+--------+----------+------+-----------+----------+---------------------+---------------------+
  13. | 1 | 1.03 s | 69 | 66 | 3 | 66.86 | 3.92 s | 56.76 ms | 0 ns | 102.38 ms | 24.13 ms | 51.06 ms | 62.45 ms |
  14. +---+--------+-------+---------+---------+------------+--------+----------+------+-----------+----------+---------------------+---------------------+
  15. [main] INFO com.blinkfox.stalker.test.StalkerTest - 任务已完成,获取最后的执行结果,并移除任务记录.
  16. +--------------------------------------------------------------------------------------------------------------------------------------------------+
  17. | duration: 2 s, concurrens: 4, warmups:5, runs: 1, printErrorLog: false |
  18. +---+--------+-------+---------+---------+------------+--------+---------+------+-----------+----------+---------------------+---------------------+
  19. | | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence |
  20. +---+--------+-------+---------+---------+------------+--------+---------+------+-----------+----------+---------------------+---------------------+
  21. | 1 | 2.03 s | 138 | 132 | 6 | 68.03 | 7.89 s | 57.2 ms | 0 ns | 102.38 ms | 24.18 ms | 53.17 ms | 61.24 ms |
  22. +---+--------+-------+---------+---------+------------+--------+---------+------+-----------+----------+---------------------+---------------------+

4. 执行指定的时间

你也可以在 run 或者 submit 方法中通过 options 参数设置运行指定的时间,当达到指定的结束时间点时,将自动停止执行中的性能测试任务。

下面是运行 15 秒,5 个绝对并发的代码示例:

  1. @Test
  2. public void submitWithDuration() {
  3. Stalker.submit(Options.ofDurationSeconds(15, 5), () -> new MyTestService().slowHello())
  4. .waitDone(StalkerFuture::get, 3000L)
  5. .done(future -> {
  6. log.info("任务已完成,获取最终的执行结果信息.");
  7. future.get();
  8. Assert.assertTrue(future.getStartNanoTime() > 0);
  9. Assert.assertTrue(future.isDone());
  10. Assert.assertEquals(future.getTotal(), future.getSuccess() + future.getFailure());
  11. Assert.assertTrue(future.getCosts() > 0);
  12. });
  13. }

执行之后将获得如下类似结果:

  1. +----------------------------------------------------------------------------------------------------------------------------------------------------+
  2. | duration: 15 s, concurrens: 5, warmups:5, runs: 1, printErrorLog: false |
  3. +---+--------+-------+---------+---------+------------+----------+----------+------+-----------+---------+---------------------+---------------------+
  4. | | Costs | Total | Success | Failure | Throughput | Sum | Avg | Min | Max | StdDev | 95% LowerConfidence | 95% UpperConfidence |
  5. +---+--------+-------+---------+---------+------------+----------+----------+------+-----------+---------+---------------------+---------------------+
  6. | 1 | 15.0 s | 1241 | 1199 | 42 | 82.71 | 1.24 min | 59.95 ms | 0 ns | 103.94 ms | 22.5 ms | 58.69 ms | 61.2 ms |
  7. +---+--------+-------+---------+---------+------------+----------+----------+------+-----------+---------+---------------------+---------------------+

5. 停止正在运行中的任务

你也可以在获取到 StalkerFuture 对象后,停止正在运行中的任务。代码示例如下:

  1. @Test
  2. public void submitWithStop() throws InterruptedException {
  3. StalkerFuture stalkerFuture = Stalker.submit(Options.of("StopTest", 200, 5, 1),
  4. () -> new MyTestService().slowHello());
  5. Assert.assertNotNull(stalkerFuture);
  6. Thread.sleep(50L);
  7. Assert.assertNotNull(stalkerFuture.getFirst());
  8. // 取消任务.
  9. boolean isCancelled = stalkerFuture.cancel();
  10. if (isCancelled) {
  11. log.info("任务已停止,获取停止前的执行结果.");
  12. } else {
  13. log.info("任务停止失败.");
  14. }
  15. stalkerFuture.done(StalkerFuture::get);
  16. }

6. 主要方法

  • List<Object> run(Runnable... runnables): 对若干个要执行的代码做性能测量评估,并返回输出结果信息.
  • List<Object> run(Options options, Runnable... runnables): 通过自定义的Options对若干个要执行的代码做性能测量评估,并返回输出结果信息.
  • MeasureResult[] runStatis(Options options, Runnable... runnables): 对若干个要执行的代码做性能测量评估,并返回多个基础测量统计结果信息.
  • StalkerFuture submit(Runnable task): 对要执行的代码做性能测量评估,并返回异步获取结果信息的 Future.
  • StalkerFuture submit(Options options, Runnable task): 通过自定义的Options对若干个要执行的代码做性能测量评估,并返回异步获取结果信息的 Future.

Options类

Options 表示做性能测量时的选项参数

1. 主要属性如下

  • name: 选项参数的名称
  • threads: 正式执行的线程数,默认为 1
  • concurrens: 正式多线程下执行的并发数,默认为 1
  • warmups: 单线程下的预热次数,默认 5
  • runs: 每个线程正式执行的次数,默认 10
  • printErrorLog: 是否打印错误日志,默认 false
  • outputs: 将测量结果通过多种方式(集合)输出出来,默认为输出到控制台,可自定义实现 MeasureOutput 接口。
  • duration: v1.2.0 版本新增,表示运行的持续时间。
  • scheduledUpdaterv1.2.0版本新增,在调用 submit 方法时会默认开启,用于定时更新统计数据的定时更新器。

2. 主要方法

以下是构造Options实例的若干重载方法:

  • Options of(String name)
  • Options of(int runs)
  • Options of(String name, int runs)
  • Options of(int threads, int concurrens)
  • Options of(String name, int threads, int concurrens)
  • Options of(String name, int threads, int concurrens, int runs)
  • Options ofDuration(long amount, TimeUnit timeUnit)
  • Options ofDuration(long amount, TimeUnit timeUnit, int concurrens)
  • Options ofDurationSeconds(long amount, int concurrens)
  • Options ofDurationMinutes(long amount, int concurrens)
  • Options ofDurationHours(long amount, int concurrens)
  • Options ofDurationDays(long amount, int concurrens)

其他方法:

  • boolean valid(): 校验 Options 相关参数是否合法
  • Options named(String name): 设置 Options 实例的 name 属性
  • Options threads(int threads): 设置 Options 实例的 threads 属性
  • Options concurrens(int concurrens): 设置 Options 实例的 concurrens 属性
  • Options warmups(int warmups): 设置 Options 实例的 warmups 属性
  • Options runs(int runs): 设置 Options 实例的 runs 属性
  • Options printErrorLog(boolean printErrorLog): 设置 Options 实例的 printErrorLog 属性
  • Options outputs(MeasureOutput... measureOutputs): 自定义设置 Options 实例的 MeasureOutput 输出通道
  • Options duration(long amount, TimeUnit timeUnit): 设置任务持续运行的时间
  • Options enableScheduledUpdater(): 默认的定时统计数据更新任务的配置选项,默认是 10
  • Options enableScheduledUpdater(long delay, TimeUnit timeUnit): 设置默认的定时统计数据更新任务的配置选项
  • Options enableScheduledUpdater(long initialDelay, long delay, TimeUnit timeUnit): 设置默认的定时统计数据更新任务的配置选项

StalkerFuture 类

  • void run(): 执行可运行的方法,通常你不需要再去手动执行了.
  • StalkerFuture waitDone(): 一直阻塞等待执行结束,v1.2.1 版本新增.
  • StalkerFuture waitDone(long period): 一直阻塞等待执行结束,可传入循环等待的间隔时间,v1.2.1 版本新增.
  • StalkerFuture waitDone(Consumer<StalkerFuture> waitPeriodConsumer): 一直阻塞等待执行结束,会每隔 1s 执行传入的可运行任务,v1.2.1 版本新增.
  • StalkerFuture waitDone(Consumer<StalkerFuture> waitPeriodConsumer, long period): 一直阻塞等待执行结束,会每隔一段指定的时间执行传入的可运行任务,v1.2.1 版本新增.
  • StalkerFuture done(Consumer<StalkerFuture> futureConsumer): 所有测量任务完成后执行的回调任务,v1.2.1 版本新增.
  • boolean cancel(): 取消正在运行中的任务.
  • boolean cancel(boolean mayInterruptIfRunning) 取消正在运行中的任务.
  • boolean isCancelled(): 是否已经取消了执行中的性能测试任务。
  • boolean isDone(): 是否已经执行完成.
  • boolean isDoneSuccessfully(): 是否是正常执行完成的.
  • Object getFirst(): 实时获取任务第一个输出通道的执行结果.
  • List<Object> get(): 实时获取任务的执行结果,该方法不会阻塞任务执行.
  • List<Object> get(long timeout, TimeUnit unit): 实时获取任务的执行结果,该方法不会阻塞任务执行.
  • MeasureResult getMeasureResult(): 获取基础的测量统计结果信息.
  • long getCosts(): 获取任务最终完成时实际所消耗的总的纳秒时间数.
  • long getTotal(): 获取当前已经运行的总次数.
  • long getSuccess(): 获取到当前时的运行成功的次数.
  • long getFailure(): 获取当前运行失败的次数.
  • long getStartNanoTime(): 获取任务开始运行时的纳秒时间戳.
  • long getEndNanoTime(): 获取任务结束运行时的纳秒时间戳,如果任务还未结束,该值将是 0.

Assert类

Assert类主要用来做断言使用。

示例

  1. Assert.assertFaster(Options.of(),
  2. () -> new MyTestService().fastHello(),
  3. () -> new MyTestService().hello());

四、许可证

stalker 类库遵守 Apache License 2.0 许可证。

五、变更日志

  • v1.2.3 修复 StalkerFuture 资源关闭不完全的 bug (2021-11-26)
    • 修复了 StalkerFuture 中 done 方法完成后资源关闭不完全的 bug
  • v1.2.2 新增了部分 API (2020-06-16)
    • 新增了和改进了部分 API;
  • v1.2.1 新增了部分 API (2020-06-15)
    • 新增了 waitDonedone 相关的 API;
    • 新增了 AsciiTableOutput 的 ASCII 表格结果输出;
  • v1.2.0 新增了异步性能评估和大量的代码重构 (2020-06-07)
    • 新增了异步提交任务作性能评估;
    • 重构了大量代码,部分方法或类与之前的版本不兼容;
  • v1.1.1 新增了吞吐量的统计指标 (2020-05-20)
    • 新增了吞吐量的统计指标;
  • v1.1.0 新增了运行后可以获取返回结果的功能 (2020-05-14)
    • 新增了 MeasureOutput 中可输出结果的功能,且默认的 run 方法,也会返回其结果;
    • 新增了 runStatis 方法,可以拿到原始的统计结果数据;
  • v1.0.1 修复线程创建过多时的限制问题 (2019-09-14)
    • 修复了线程池超过一定数量后的线程创建失败的问题;
  • v1.0.0 里程碑正式版 (2019-02-08)
    • 完成了基准性能测试所需的基础功能;