一 什麼是 JMH
JMH 是在 method 層面上的 benchmark,精度可以精確到微秒級,是對熱點函數進行優化時,對優化結果進行定量分析的工具。
二 JMH 的應用場景
典型場景:
- 想定量地知道某個函數需要執行多長時間,以及執行時間和輸入 n 的相關性。
- 一個函數有多種不同的實現,針對多種不同的實現,需要定量分析出那種實現性能更好。
三 JMH 的使用
3.1 引入依賴
<dependencies>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.14.1</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.14.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
3.2 使用介紹
下面樣例是比較一下 AtomicLong 和 LongAdder 的性能:
package com.sb.springbootjmh;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAdder;
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Mode.Throughput)
@State(Scope.Thread)
public class Main {
private static AtomicLong count = new AtomicLong();
private static LongAdder longAdder = new LongAdder();
public static void main(String[] args) throws Exception {
Options options = new OptionsBuilder()
.include(Main.class.getName())
.forks(1)
.warmupIterations(5)
.measurementIterations(5)
.build();
new Runner(options).run();
}
@Benchmark
@Threads(10)
public void run0(){
count.getAndIncrement();
}
@Benchmark
@Threads(10)
public void run1(){
longAdder.increment();
}
}
得到如下結果:
3.3 JMH註解說明
@Benchmark
表示該方法是需要進行 benchmark 的對象,用法和 JUnit 的 @Test
類似。其中 Mode 有四種選擇:
Throughput
: 整體吞吐量,例如“1秒內可以執行多少次調用”。AverageTime
: 調用的平均時間,例如“每次調用平均耗時xxx毫秒”。SampleTime
: 隨機取樣,最後輸出取樣結果的分佈,例如“99%的調用在xxx毫秒以內,99.99%的調用在xxx毫秒以內”SingleShotTime
: 以上模式都是默認一次 iteration 是 1s,唯有SingleShotTime
是只運行一次。往往同時把 warmup 次數設爲0,用於測試冷啓動時的性能。
@State
State
用於聲明某個類是一個“狀態”,然後接受一個 Scope
參數用來表示該狀態的共享範圍。因爲很多 benchmark 會需要一些表示狀態的類,JMH 允許你把這些類以依賴注入的方式注入到 benchmark 函數裏。Scope
主要分爲兩種。
Thread
: 該狀態爲每個線程獨享。Benchmark
: 該狀態在所有線程間共享。
@OutputTimeUnit
benchmark 結果所使用的時間單位。
3.4 JMH 啓動參數說明
Options options = new OptionsBuilder()
.include(Main.class.getName())
.forks(1)
.warmupIterations(5)
.measurementIterations(5)
.build();
- include
benchmark 所在的類的名字,注意這裏是使用正則表達式對所有類進行匹配的。
- fork
進行 fork 的次數。如果 fork 數是2的話,則 JMH 會 fork 出兩個進程來進行測試。
- warmupIterations
預熱的迭代次數。
- measurementIterations
實際測量的迭代次數。
四 參考說明
本文只是簡單的介紹 JMH 的使用,要想深入瞭解請參考如下鏈接: