Skip to content

A small library for performance evaluation of Java code. 这是一个用来对Java代码做性能评估的工具库。

License

Notifications You must be signed in to change notification settings

blinkfox/stalker

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

77 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

stalker

Hits Build Status GitHub license codecov Java Version

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

一、特性

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

二、快速集成

1. Maven

<dependency>
    <groupId>com.blinkfox</groupId>
    <artifactId>stalker</artifactId>
    <version>1.2.3</version>
</dependency>

2. Gradle

compile 'com.blinkfox:stalker:1.2.3'

三、API 介绍和使用

预先准备

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

/**
 * 用于测量(仅测试使用)该类中的方法的执行耗时的类.
 *
 * @author blinkfox on 2019-02-03.
 */
@Slf4j
public class MyTestService {

    /**
     * 测试方法1,模拟业务代码耗时 2~5 ms,且会有约 1% 的几率执行异常.
     */
    public void hello() {
        // 模拟运行时抛出异常.
        if (new Random().nextInt(100) == 5) {
            throw new MyServiceException("My Service Exception.");
        }

        // 模拟运行占用约 2~5 ms 的时间.
        this.sleep(2L + new Random().nextInt(3));
    }

    /**
     * 测试方法2,模拟业务代码运行占用约 2 ms 的时间.
     */
    public void fastHello() {
        this.sleep(2L);
    }

    /**
     * 本线程调用该方法时,睡眠指定时间,用来模拟业务耗时.
     *
     * @param time 时间
     */
    private void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            log.info("InterruptedException", e);
            Thread.currentThread().interrupt();
        }
    }

}

Stalker 类

1. 最简示例

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

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

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

+------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                         threads: 1, concurrens: 1, warmups:5, runs: 10, printErrorLog: false                                         |
+---+----------+-------+---------+---------+------------+----------+---------+---------+---------+---------+---------------------+---------------------+
|   |  Costs   | Total | Success | Failure | Throughput |   Sum    |   Avg   |   Min   |   Max   | StdDev  | 95% LowerConfidence | 95% UpperConfidence |
+---+----------+-------+---------+---------+------------+----------+---------+---------+---------+---------+---------------------+---------------------+
| 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       |
+---+----------+-------+---------+---------+------------+----------+---------+---------+---------+---------+---------------------+---------------------+

也可以获取到统计结果:

// 获取运行的统计结果.
MeasureResult[] measureResults = mStalker.runStatis(() -> new MyTestService().hello());

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

2. 更全示例

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

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

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

+-------------------------------------------------------------------------------------------------------------------------------------------------------+
|                                       threads: 1000, concurrens: 200, warmups:10, runs: 5, printErrorLog: false                                       |
+---+-----------+-------+---------+---------+------------+---------+---------+---------+----------+---------+---------------------+---------------------+
|   |   Costs   | Total | Success | Failure | Throughput |   Sum   |   Avg   |   Min   |   Max    | StdDev  | 95% LowerConfidence | 95% UpperConfidence |
+---+-----------+-------+---------+---------+------------+---------+---------+---------+----------+---------+---------------------+---------------------+
| 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       |
| 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       |
+---+-----------+-------+---------+---------+------------+---------+---------+---------+----------+---------+---------------------+---------------------+

结果说明:

  • 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 并发下的异步执行情况:

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

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

+-------------------------------------------------------------------------------------------------------------------------------------+
|                               duration: 2 s, concurrens: 4, warmups:5, runs: 1, printErrorLog: false                                |
+---+-------+-------+---------+---------+------------+------+------+------+------+--------+---------------------+---------------------+
|   | Costs | Total | Success | Failure | Throughput | Sum  | Avg  | Min  | Max  | StdDev | 95% LowerConfidence | 95% UpperConfidence |
+---+-------+-------+---------+---------+------------+------+------+------+------+--------+---------------------+---------------------+
| 1 | 0 ns  |   0   |    0    |    0    |    0.00    | 0 ns | 0 ns | 0 ns | 0 ns |  0 ns  |        0 ns         |        0 ns         |
+---+-------+-------+---------+---------+------------+------+------+------+------+--------+---------------------+---------------------+

+---------------------------------------------------------------------------------------------------------------------------------------------------+
|                                      duration: 2 s, concurrens: 4, warmups:5, runs: 1, printErrorLog: false                                       |
+---+--------+-------+---------+---------+------------+--------+----------+------+-----------+----------+---------------------+---------------------+
|   | Costs  | Total | Success | Failure | Throughput |  Sum   |   Avg    | Min  |    Max    |  StdDev  | 95% LowerConfidence | 95% UpperConfidence |
+---+--------+-------+---------+---------+------------+--------+----------+------+-----------+----------+---------------------+---------------------+
| 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       |
+---+--------+-------+---------+---------+------------+--------+----------+------+-----------+----------+---------------------+---------------------+

[main] INFO com.blinkfox.stalker.test.StalkerTest - 任务已完成,获取最后的执行结果,并移除任务记录.
+--------------------------------------------------------------------------------------------------------------------------------------------------+
|                                      duration: 2 s, concurrens: 4, warmups:5, runs: 1, printErrorLog: false                                      |
+---+--------+-------+---------+---------+------------+--------+---------+------+-----------+----------+---------------------+---------------------+
|   | Costs  | Total | Success | Failure | Throughput |  Sum   |   Avg   | Min  |    Max    |  StdDev  | 95% LowerConfidence | 95% UpperConfidence |
+---+--------+-------+---------+---------+------------+--------+---------+------+-----------+----------+---------------------+---------------------+
| 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       |
+---+--------+-------+---------+---------+------------+--------+---------+------+-----------+----------+---------------------+---------------------+

4. 执行指定的时间

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

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

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

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

+----------------------------------------------------------------------------------------------------------------------------------------------------+
|                                      duration: 15 s, concurrens: 5, warmups:5, runs: 1, printErrorLog: false                                       |
+---+--------+-------+---------+---------+------------+----------+----------+------+-----------+---------+---------------------+---------------------+
|   | Costs  | Total | Success | Failure | Throughput |   Sum    |   Avg    | Min  |    Max    | StdDev  | 95% LowerConfidence | 95% UpperConfidence |
+---+--------+-------+---------+---------+------------+----------+----------+------+-----------+---------+---------------------+---------------------+
| 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       |
+---+--------+-------+---------+---------+------------+----------+----------+------+-----------+---------+---------------------+---------------------+

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

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

@Test
public void submitWithStop() throws InterruptedException {
    StalkerFuture stalkerFuture = Stalker.submit(Options.of("StopTest", 200, 5, 1),
            () -> new MyTestService().slowHello());
    Assert.assertNotNull(stalkerFuture);

    Thread.sleep(50L);
    Assert.assertNotNull(stalkerFuture.getFirst());

    // 取消任务.
    boolean isCancelled = stalkerFuture.cancel();
    if (isCancelled) {
        log.info("任务已停止,获取停止前的执行结果.");
    } else {
        log.info("任务停止失败.");
    }

    stalkerFuture.done(StalkerFuture::get);
}

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类主要用来做断言使用。

示例

Assert.assertFaster(Options.of(),
        () -> new MyTestService().fastHello(),
        () -> 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)
    • 完成了基准性能测试所需的基础功能;

About

A small library for performance evaluation of Java code. 这是一个用来对Java代码做性能评估的工具库。

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages