1. 程式人生 > 實用技巧 >在java中使用JMH(Java Microbenchmark Harness)做效能測試

在java中使用JMH(Java Microbenchmark Harness)做效能測試

1.在java中使用JMH(Java Microbenchmark Harness)做效能測試

https://www.cnblogs.com/flydean/p/12680265.html

2.springboot整合JMH基準測試, 以及錯誤JMH had finished, but forked VM did not exit, 解決辦法

https://blog.csdn.net/shuaixingrumo/article/details/104884757

文章目錄


在java中使用JMH(Java Microbenchmark Harness)做效能測試

JMH的全稱是Java Microbenchmark Harness,是一個open JDK中用來做效能測試的套件。該套件已經被包含在了JDK 12中。

本文將會講解如何使用JMH來在java中做效能測試。

如果你使用的不是JDK 12,那麼需要新增如下依賴:

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.19</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.19</version>
</dependency>

使用JMH做效能測試

如果我們想測試某個方法的效能,一般來說就是重複執行某個方法n次,求出總的執行時間,然後求平均值。

但是這樣通常會有一些問題,比如程式的頭幾次執行通常會比較慢,因為JVM會對多次執行的程式碼進行優化。另外得出的統計結果也不夠直觀,需要我們自行解析。

如果使用JMH可以輕鬆解決這些問題。

在JMH中,將要測試的方法新增@Benchmark註解即可:

    @Benchmark
    public void measureThroughput() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(100);
    }

看下怎麼呼叫:

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(BenchMarkUsage.class.getSimpleName())
//                .include(BenchMarkUsage.class.getSimpleName()+".*measureThroughput*")
                // 預熱3輪
                .warmupIterations(3)
                // 度量5輪
                .measurementIterations(5)
                .forks(1)
                .build();

        new Runner(opt).run();
    }

上面的例子,我們通過OptionsBuilder的include方法添加了需要進行測試的類。

預設情況下,該類的所有@Benchmark方法都將會被測試,如果我們只想測試其中的某個方法,我們可以在類後面加上方法的名字:

.include(BenchMarkUsage.class.getSimpleName()+".*measureAll*")

上面的程式碼支援萬用字元。

warmupIterations(3)意思是在真正的執行前,先熱身三次。

measurementIterations(5)表示我們將方法執行5次來測試效能。

forks(1)表示啟動一個程序來執行這個任務。

上面是最基本的執行,我們看下執行結果:

# JMH version: 1.19
# VM version: JDK 1.8.0_171, VM 25.171-b11
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA 2.app/Contents/lib/idea_rt.jar=55941:/Applications/IntelliJ IDEA 2.app/Contents/bin -Dfile.encoding=UTF-8
# Warmup: 3 iterations, 1 s each
# Measurement: 5 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.flydean.BenchMarkUsage.measureThroughput

# Run progress: 26.66% complete, ETA 00:01:42
# Fork: 1 of 1
# Warmup Iteration   1: 9.727 ops/s
# Warmup Iteration   2: 9.684 ops/s
# Warmup Iteration   3: 9.678 ops/s
Iteration   1: 9.652 ops/s
Iteration   2: 9.678 ops/s
Iteration   3: 9.733 ops/s
Iteration   4: 9.651 ops/s
Iteration   5: 9.678 ops/s


Result "com.flydean.BenchMarkUsage.measureThroughput":
  9.678 ±(99.9%) 0.129 ops/s [Average]
  (min, avg, max) = (9.651, 9.678, 9.733), stdev = 0.034
  CI (99.9%): [9.549, 9.808] (assumes normal distribution)

ops/s 是每秒的OPS次數。程式會給出執行的最小值,平均值和最大值。同時給出標準差stdev和置信區間CI。

BenchmarkMode

上面的例子中, 我們只用了最簡單的@Benchmark。如果想實現更加複雜和自定義的BenchMark,我們可以使用@BenchmarkMode。

先舉個例子:

    @Benchmark
    @BenchmarkMode(Mode.Throughput)
    @OutputTimeUnit(TimeUnit.SECONDS)
    public void measureThroughput() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(100);
    }

上面的例子中,我們指定了@BenchmarkMode(Mode.Throughput),Throughput的意思是整體吞吐量,表示給定的時間內執行的次數。

這裡我們通過 @OutputTimeUnit(TimeUnit.SECONDS)來指定時間單位。

Mode除了Throughput還有如下幾種模式:

  • AverageTime - 呼叫的平均時間
  • SampleTime - 隨機取樣,最後輸出取樣結果的分佈
  • SingleShotTime - 只會執行一次,通常用來測試冷啟動時候的效能。
  • All - 所有的benchmark modes。

Fork和Warmup

上面的例子中我們通過程式碼來顯式的制定Fork和Warmup,我們也可以使用註解來實現:

    @Fork(value = 1, warmups = 2)
    @Warmup(iterations = 5)

上面的例子中value表示該benchMark執行多少次,warmups表示fork多少個程序來執行。iterations表示warmup的iterations個數。

如果你同時在程式碼中和註解中都配置了相關的資訊,那麼註解將會覆蓋掉程式碼中的顯示配置。

State和Scope

如果我們在多執行緒環境中使用beachMark,那麼多執行緒中用到的類變數是共享還是每個執行緒一個呢?

這個時候我們就要用到@State註解。

@State(Scope.Benchmark)
public class StateUsage {
}

Scope有三種:

  • Scope.Thread:預設的State,每個測試執行緒分配一個例項;
  • Scope.Benchmark:所有測試執行緒共享一個例項,用於測試有狀態例項在多執行緒共享下的效能;
  • Scope.Group:每個執行緒組共享一個例項;

本文的例子可以參考https://github.com/ddean2009/learn-java-concurrency/tree/master/benchmark

更多教程請參考flydean的部落格

分類:併發與多執行緒,java 標籤:java,thread,併發,多執行緒
«上一篇:java中CyclicBarrier的使用
»下一篇:java中ThreadLocalRandom的使用