JMH性能測試(轉(zhuǎn))

基于原貼,用Markdown語法美化了一下赚哗。
原貼:https://www.cnblogs.com/bestzhang/p/10082119.html

JMH性能測試

JMH腕让,即Java Microbenchmark Harness 翻譯:java 微基準(zhǔn)測試 工具套件务荆。

1.添加依賴

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

2.第一個例子

請參加: JMHFirstBenchmark.java

請參加(晉級): SecondBenchmark.java
請參加(晉級): ThirdBenchmark.java

3.常用注解說明

3.1 @BenchmarkMode(Mode.All)

Mode有:- Throughput: 整體吞吐量,例如“1秒內(nèi)可以執(zhí)行多少次調(diào)用” (thrpt,參加第5點)

  • AverageTime: 調(diào)用的平均時間懦趋,例如“每次調(diào)用平均耗時xxx毫秒”。(avgt)
  • SampleTime: 隨機(jī)取樣疹味,最后輸出取樣結(jié)果的分布仅叫,例如“99%的調(diào)用在xxx毫秒以內(nèi)帜篇,99.99%的調(diào)用在xxx毫秒以內(nèi)”(simple)
  • SingleShotTime: 以上模式都是默認(rèn)一次 iteration 是 1s,唯有 SingleShotTime 是只運行一次诫咱。往往同時把 warmup 次數(shù)設(shè)為0坠狡,用于測試?yán)鋯訒r的性能。(ss)

3.2 @OutputTimeUnit(TimeUnit.MILLISECONDS)

統(tǒng)計單位遂跟, 微秒逃沿、毫秒 、分幻锁、小時凯亮、天

3.3 @State

可參:JMHFirstBenchmark.java

類注解,JMH測試類必須使用@State注解哄尔,State定義了一個類實例的生命周期假消,可以類比Spring Bean的Scope。由于JMH允許多線程同時執(zhí)行測試岭接,不同的選項含義如下:

Scope.Thread:默認(rèn)的State富拗,每個測試線程分配一個實例;
Scope.Benchmark:所有測試線程共享一個實例鸣戴,用于測試有狀態(tài)實例在多線程共享下的性能啃沪;
Scope.Group:每個線程組共享一個實例;

3.4 @Benchmark

很重要的方法注解窄锅,表示該方法是需要進(jìn)行 benchmark 的對象创千。和@test 注解一致

3.5 @Setup

方法注解,會在執(zhí)行 benchmark 之前被執(zhí)行入偷,正如其名追驴,主要用于初始化。

3.6 @TearDown (Level)

方法注解疏之,與@Setup 相對的殿雪,會在所有 benchmark 執(zhí)行結(jié)束以后執(zhí)行,主要用于資源的回收等锋爪。
(Level) 用于控制 @Setup丙曙,@TearDown 的調(diào)用時機(jī),默認(rèn)是 Level.Trial几缭。

      Trial:每個benchmark方法前后河泳;
      Iteration:每個benchmark方法每次迭代前后;
      Invocation:每個benchmark方法每次調(diào)用前后年栓,謹(jǐn)慎使用拆挥,需留意javadoc注釋;  

3.7 @Param

@Param注解接收一個String數(shù)組 ,
可以用來指定某項參數(shù)的多種情況纸兔。特別適合用來測試一個函數(shù)在不同的參數(shù)輸入的情況下的性能惰瓜。
可參:JMHFirstBenchmark.java

4 Options常用選項

4.1 include

benchmark 所在的類的名字,這里可以使用正則表達(dá)式對所有類進(jìn)行匹配汉矿。
參考:SecondBenchmark.java

4.2 fork

JVM因為使用了profile-guided optimization而“臭名昭著”崎坊,這對于微基準(zhǔn)測試來說十分不友好,因為不同測試方法的profile混雜在一起洲拇,“互相傷害”彼此的測試結(jié)果奈揍。對于每個@Benchmark方法使用一個獨立的進(jìn)程可以解決這個問題,這也是JMH的默認(rèn)選項赋续。注意不要設(shè)置為0男翰,設(shè)置為n則會啟動n個進(jìn)程執(zhí)行測試(似乎也沒有太大意義)。
fork選項也可以通過方法注解以及啟動參數(shù)來設(shè)置纽乱。

4.3 warmupIterations

預(yù)熱次數(shù)蛾绎,每次默認(rèn)1秒。

4.4 measurementIterations

實際測量的迭代次數(shù)鸦列,每次默認(rèn)1秒租冠。

4.5 Group

方法注解,可以把多個 benchmark 定義為同一個 group薯嗤,則它們會被同時執(zhí)行顽爹,譬如用來模擬生產(chǎn)者-消費者讀寫速度不一致情況下的表現(xiàn)。

4.6 Threads

每個fork進(jìn)程使用多少條線程去執(zhí)行你的測試方法应民,默認(rèn)值是Runtime.getRuntime().availableProcessors()话原。

5 輸出結(jié)果

# @BenchmarkMode(Mode.All)
# JMH version: 1.19
# VM version: JDK 1.7.0_80, VM 24.80-b11
# VM invoker: C:\Program Files\Java\jdk1.7.0_80\jre\bin\java.exe
# VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.1\lib\idea_rt.jar=51664:D:\Program Files\JetBrains\IntelliJ IDEA 2018.1\bin -Dfile.encoding=UTF-8
# Warmup: 2 iterations, single-shot each
# Measurement: 2 iterations, single-shot each
# Timeout: 10 min per iteration
# Threads: 10 threads
# Benchmark mode: Single shot invocation time
# Benchmark: com.gemantic.wealth.yunmatong.service.jmh.SecondBenchmark.singleThreadBench
# Parameters: (length = 100000)

# Run progress: 99.98% complete, ETA 00:00:00
# Fork: 1 of 1
# Warmup Iteration   1: 34.641 ±(99.9%) 33.844 ms/op
# Warmup Iteration   2: 7.129 ±(99.9%) 9.238 ms/op
Iteration   1: 7.573 ±(99.9%) 4.581 ms/op
Iteration   2: 6.235 ±(99.9%) 4.150 ms/op



# Run complete. Total time: 00:00:36

Benchmark                                                    (length)    Mode     Cnt    Score    Error   Units
SecondBenchmark.multiThreadBench                               100000   thrpt       2  147.758           ops/ms    
SecondBenchmark.singleThreadBench                              100000   thrpt       2    0.983           ops/ms
SecondBenchmark.multiThreadBench                               100000    avgt       2    0.068            ms/op
SecondBenchmark.singleThreadBench                              100000    avgt       2   10.510            ms/op
SecondBenchmark.multiThreadBench                               100000  sample  295532    0.068 ±  0.001   ms/op
SecondBenchmark.multiThreadBench:multiThreadBench·p0.00        100000  sample            0.010            ms/op
SecondBenchmark.multiThreadBench:multiThreadBench·p0.50        100000  sample            0.066            ms/op
SecondBenchmark.multiThreadBench:multiThreadBench·p0.90        100000  sample            0.095            ms/op
SecondBenchmark.multiThreadBench:multiThreadBench·p0.95        100000  sample            0.104            ms/op
SecondBenchmark.multiThreadBench:multiThreadBench·p0.99        100000  sample            0.126            ms/op
SecondBenchmark.multiThreadBench:multiThreadBench·p0.999       100000  sample            0.172            ms/op
SecondBenchmark.multiThreadBench:multiThreadBench·p0.9999      100000  sample            1.729            ms/op
SecondBenchmark.multiThreadBench:multiThreadBench·p1.00        100000  sample            4.309            ms/op
SecondBenchmark.singleThreadBench                              100000  sample    2036   10.196 ±  0.581   ms/op
SecondBenchmark.singleThreadBench:singleThreadBench·p0.00      100000  sample            6.201            ms/op
SecondBenchmark.singleThreadBench:singleThreadBench·p0.50      100000  sample            8.020            ms/op
SecondBenchmark.singleThreadBench:singleThreadBench·p0.90      100000  sample           10.355            ms/op
SecondBenchmark.singleThreadBench:singleThreadBench·p0.95      100000  sample           38.443            ms/op
SecondBenchmark.singleThreadBench:singleThreadBench·p0.99      100000  sample           41.943            ms/op
SecondBenchmark.singleThreadBench:singleThreadBench·p0.999     100000  sample           73.498            ms/op
SecondBenchmark.singleThreadBench:singleThreadBench·p0.9999    100000  sample           74.973            ms/op
SecondBenchmark.singleThreadBench:singleThreadBench·p1.00      100000  sample           74.973            ms/op
SecondBenchmark.multiThreadBench                               100000      ss       2    0.223            ms/op
SecondBenchmark.singleThreadBench                              100000      ss       2    6.904            ms/op

6 第一個例子

package com.gemantic.wealth.yunmatong.service.jmh;
 
import lombok.extern.slf4j.Slf4j;
import org.openjdk.jmh.annotations.*;
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 org.openjdk.jmh.runner.options.TimeValue;
 
import java.util.concurrent.TimeUnit;
 
@Slf4j
@BenchmarkMode(Mode.AverageTime)// 測試方法平均執(zhí)行時間
@OutputTimeUnit(TimeUnit.MICROSECONDS)// 輸出結(jié)果的時間粒度為微秒
@State(Scope.Benchmark) // 每個測試線程一個實例
public class JMHFirstBenchmark {
    /*
     * Most of the time, you need to maintain some state while the benchmark is
     * running. Since JMH is heavily used to build concurrent benchmarks, we
     * opted for an explicit notion of state-bearing objects.
     *
     * Below are two state objects. Their class names are not essential, it
     * matters they are marked with @State. These objects will be instantiated
     * on demand, and reused during the entire benchmark trial.
     *
     * The important property is that state is always instantiated by one of
     * those benchmark threads which will then have the access to that state.
     * That means you can initialize the fields as if you do that in worker
     * threads (ThreadLocals are yours, etc).
     */
 
    @State(Scope.Benchmark)
    public static class BenchmarkState {
        volatile double x = Math.PI;
    }
 
    @State(Scope.Thread)
    public static class ThreadState {
        volatile double x = Math.PI;
    }
 
    @Benchmark
    public void measureUnshared(ThreadState state) {
        // All benchmark threads will call in this method.
        //
        // However, since ThreadState is the Scope.Thread, each thread
        // will have it's own copy of the state, and this benchmark
        // will measure unshared case.
        state.x++;
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("measureUnshared:"+ state.x);
    }
 
    @Benchmark
    public void measureShared(BenchmarkState state) {
        // All benchmark threads will call in this method.
        //
        // Since BenchmarkState is the Scope.Benchmark, all threads
        // will share the state instance, and we will end up measuring
        // shared case.
        state.x++;
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("measureShared:"+ state.x);
    }
 
    /*
     * ============================== HOW TO RUN THIS TEST: ====================================
     *
     * You are expected to see the drastic difference in shared and unshared cases,
     * because you either contend for single memory location, or not. This effect
     * is more articulated on large machines.
     *
     * You can run this test:
     *
     * a) Via the command line:
     *    $ mvn clean install
     *    $ java -jar target/benchmarks.jar JMHSample_03 -wi 5 -i 5 -t 4 -f 1
     *    (we requested 5 measurement/warmup iterations, with 4 threads, single fork)
     *
     * b) Via the Java API:
     *    (see the JMH homepage for possible caveats when running from IDE:
     *      http://openjdk.java.net/projects/code-tools/jmh/)
     */
 
    public static void main(String[] args) throws RunnerException {
        // 可以通過注解
        Options opt = new OptionsBuilder()
                .include(JMHFirstBenchmark.class.getSimpleName())
                .warmupIterations(3) // 預(yù)熱3次
                .measurementIterations(2).measurementTime(TimeValue.valueOf("1s")) // 運行5次,每次10秒
                .threads(10) // 10線程并發(fā)
                .forks(2)
                .build();
 
        new Runner(opt).run();
    }
 
}

7 第二個例子

setup&TearDown&Param

package com.gemantic.wealth.yunmatong.service.jmh;
 
import com.gemantic.wealth.yunmatong.service.jmh.service.Calculator;
import com.gemantic.wealth.yunmatong.service.jmh.service.MultithreadCalculator;
import com.gemantic.wealth.yunmatong.service.jmh.service.SinglethreadCalculator;
import org.openjdk.jmh.annotations.*;
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.util.concurrent.TimeUnit;
 
@BenchmarkMode(Mode.All)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Benchmark)
public class SecondBenchmark {
    @Param({"100000"})
    private int length;
  
    private int[] numbers;
    private Calculator singleThreadCalc;
    private Calculator multiThreadCalc;
  
    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(SecondBenchmark.class.getSimpleName()) // .include("JMHF.*") 可支持正則
                .forks(0)
                .warmupIterations(2)
                .measurementIterations(2).threads(10)
                .build();
  
        new Runner(opt).run();
    }
 
    @Benchmark
    public long singleThreadBench() {
        return singleThreadCalc.sum(numbers);
    }
  
    @Benchmark
    public long multiThreadBench() {
        return multiThreadCalc.sum(numbers);
    }
  
    @Setup(Level.Trial)
    public void prepare() {
        int n = length;
        numbers =new int[n];
        for (int i=0;i<n;i++){
            numbers[i]=i;
        }
        singleThreadCalc = new SinglethreadCalculator();
        multiThreadCalc = new MultithreadCalculator(Runtime.getRuntime().availableProcessors());
    }
 
 
    @TearDown
    public void shutdown() {
        singleThreadCalc.shutdown();
        multiThreadCalc.shutdown();
    }
}

8 第三個例子

group

package com.gemantic.wealth.yunmatong.service.jmh;
 
import com.gemantic.wealth.yunmatong.service.jmh.service.Calculator;
import com.gemantic.wealth.yunmatong.service.jmh.service.MultithreadCalculator;
import com.gemantic.wealth.yunmatong.service.jmh.service.SinglethreadCalculator;
import org.openjdk.jmh.annotations.*;
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 org.openjdk.jmh.runner.options.TimeValue;
 
import java.util.concurrent.TimeUnit;
 
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
public class ThirdBenchmark {
 
    @State(Scope.Group)
    public static class BenchmarkState {
        volatile double x = Math.PI;
    }
 
    @Benchmark
    @Group("custom")
    @GroupThreads(10)
    public void read(BenchmarkState state) {
        state.x++;
        try {
            Thread.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("ThirdBenchmark.read: "+ state.x);
    }
 
    @Benchmark
    @Group("custom")
    public void book(BenchmarkState state) {
        state.x++;
        try {
            Thread.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("ThirdBenchmark.book: "+ state.x);
    }
 
 
    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(ThirdBenchmark.class.getSimpleName()) // .include("JMHF.*") 可支持正則
                .forks(0)
                .warmupIterations(0)
                .measurementIterations(2).measurementTime(TimeValue.valueOf("10ms")).threads(5)
                .build();
 
        new Runner(opt).run();
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诲锹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子涉馅,更是在濱河造成了極大的恐慌归园,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稚矿,死亡現(xiàn)場離奇詭異庸诱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)晤揣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進(jìn)店門桥爽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人昧识,你說我怎么就攤上這事钠四。” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵缀去,是天一觀的道長侣灶。 經(jīng)常有香客問我,道長缕碎,這世上最難降的妖魔是什么褥影? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮咏雌,結(jié)果婚禮上凡怎,老公的妹妹穿的比我還像新娘。我一直安慰自己赊抖,他們只是感情好统倒,可當(dāng)我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著熏迹,像睡著了一般檐薯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上注暗,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天坛缕,我揣著相機(jī)與錄音,去河邊找鬼捆昏。 笑死赚楚,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骗卜。 我是一名探鬼主播宠页,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼寇仓!你這毒婦竟也來了举户?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤遍烦,失蹤者是張志新(化名)和其女友劉穎俭嘁,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體服猪,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡供填,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了罢猪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片近她。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖膳帕,靈堂內(nèi)的尸體忽然破棺而出粘捎,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響仇参,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蓬痒,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漆羔。 院中可真熱鬧梧奢,春花似錦、人聲如沸演痒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鸟顺。三九已至惦蚊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間讯嫂,已是汗流浹背蹦锋。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留欧芽,地道東北人莉掂。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像千扔,于是被迫代替她去往敵國和親憎妙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容