Java中Gradle结合JMH实现性能测试

如果您想对代码进行基准测试,Java Microbenchmark Harness 是首选工具。在我们的示例中,我们将使用refill-rate-limiter项目

由于 refill-rate-limiter 使用Gradle,我们将使用以下gradle插件

plugins {
...
  id "me.champeau.gradle.jmh" version "0.5.3"
...

我们将基准放在 jmh/java/io/github/resilience4j/ratelimiter 文件夹中。
我们的基准应该是这样的。

package io.github.resilience4j.ratelimiter;
 
import io.github.resilience4j.ratelimiter.internal.RefillRateLimiter;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.profile.GCProfiler;
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.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
 
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Mode.All)
public class RateLimiterBenchmark {
 
    private static final int FORK_COUNT = 2;
    private static final int WARMUP_COUNT = 10;
    private static final int ITERATION_COUNT = 10;
    private static final int THREAD_COUNT = 2;
 
    private RefillRateLimiter refillRateLimiter;
 
    private Supplier<String> refillGuardedSupplier;
 
    public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .addProfiler(GCProfiler.class)
                .build();
        new Runner(options).run();
    }
 
    @Setup
    public void setUp() {
 
        RefillRateLimiterConfig refillRateLimiterConfig = RefillRateLimiterConfig.custom()
                                                                                 .limitForPeriod(1)
                                                                                 .limitRefreshPeriod(Duration.ofNanos(1))
                                                                                 .timeoutDuration(Duration.ofSeconds(5))
                                                                                 .build();
 
        refillRateLimiter = new RefillRateLimiter("refillBased", refillRateLimiterConfig);
 
        Supplier<String> stringSupplier = () -> {
            Blackhole.consumeCPU(1);
            return
"Hello Benchmark";
        };
 
        refillGuardedSupplier = RateLimiter.decorateSupplier(refillRateLimiter, stringSupplier);
    }
 
    @Benchmark
    @Threads(value = THREAD_COUNT)
    @Warmup(iterations = WARMUP_COUNT)
    @Fork(value = FORK_COUNT)
    @Measurement(iterations = ITERATION_COUNT)
    public String refillPermission() {
        return refillGuardedSupplier.get();
    }
 
}

通过使用基准范围,基准范围上使用的所有线程将共享同一对象。我们这样做是因为我们想要测试 refill-rate-limiter 在多线程场景中的执行情况。
@State(Scope.Benchmark)

我们希望以微秒为单位报告结果,因此我们将使用 OutputTimeUnit。
@OutputTimeUnit(TimeUnit.MICROSECONDS)

在 JMH 上,我们有各种基准模式,具体取决于我们想要测量的内容。

  • 吞吐量是指我们想要测量单位时间的操作数量。
  • AverageTime 当我们想要测量每个操作的平均时间时。
  • SampleTime 当我们想要对每个操作的时间进行采样时,包括最小、最大时间,而不仅仅是平均值。
  • SingleShotTime:当我们想要测量单个操作的时间时。当我们想要确定操作在冷启动时如何执行时,这会有所帮助。

我们还可以选择测量上述所有内容。
@BenchmarkMode(Mode.All)

在类级别配置的那些选项将应用于我们将添加的基准方法。

我们还检查一下基准测试将如何运行

我们将使用 Threads 注释指定线程数。
@Threads(value = THREAD_COUNT)

此外,我们希望在运行实际基准测试之前进行热身。这样我们的代码将被初始化,在线优化将发生,并且我们的运行时将在运行基准测试之前适应条件。
@Warmup(iterations = WARMUP_COUNT)

使用 Fork,我们将指示基准测试将运行多少次。
@Fork(value = FORK_COUNT)

然后我们需要指定我们想要测量的迭代次数/
@Measurement(iterations = ITERATION_COUNT)

我们可以通过使用来开始我们的测试
gradle jmh
结果将保存在文件中。