日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

任何新工具的出現,都是為了解決某個具體問題而誕生的,否則就沒有存在的必要了

本文章將會概述一下 基準測試的概念 、StopWatch的基本使用、 JMH的基本使用與用戶JMH中常用注解概述。

Java JMH 基準測試工具

 

 

1 引言

JMH 全稱 JAVA Microbenchmark Harness ,Microbenchmark 可解析為 短語 micro-benchmark 測試,Microbenchmark也可解析為 micro(基本的)benchmark(標準檢查程序) 。

JMH 是由 Java Jvm 虛擬機團隊開發 ,在Jvm 對 Java 文件的編譯階段、類的加載階段、運行階段者有持續的不同程度的優化,JMH的誕生就是為了讓 Java 開發者能夠了解到自己所編寫的代碼運行的情況,以及性能方面的情況。

1.1 基準測試 ?

基準測試是指通過設計科學的測試方法、測試工具和測試系統,實現對一類測試對象的某項性能指標進行定量的和可對比的測試。

1.2 使用 StopWatch 來進行測試時間計算

一個常見的問題 就是 我們會說 ArrayList 比 LinkedList 性能好點,那么我們總會要想方法去測試一下,如添加 1000 0000 條數據,看誰消耗的時間少, StopWatch 用來記錄這個時間差并可生成對比,如下代碼清單 1-1 中所示的測試用例中,分別向 ArrayList 、LinkedList 中添加了 1000 0000 條數據,然后通過 StopWatch 來生成時間消耗對比:

///代碼清單 1-1 
package com.example.demo;import org.junit.jupiter.api.Test;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.util.StopWatch;import java.util.ArrayList;import java.util.LinkedList;import java.util.List;@SpringBootTestclass DemoApplicationTest2 {  private static final Logger LOG = LoggerFactory.getLogger(DemoApplicationTest2.class);
  @Test  void testArrayAndLinkedList() {    List<String> arrayList = new ArrayList<>();    StopWatch stopWatch = new StopWatch();    //開始計時
    stopWatch.start("arrayList 測試");
    for (int i = 0; i < 10000000; i++) {
      arrayList.add("測試數據");
    }    ///停止計時
    stopWatch.stop();        //測試 LinkedList
    List<String> linkedList = new LinkedList<>();    //開始計時
    stopWatch.start("linkedList 測試");
    for (int i = 0; i < 10000000; i++) {
      linkedList.add("測試數據");
    }    ///停止計時
    stopWatch.stop();            LOG.info("arrayList 消耗的總時間 " + stopWatch.prettyPrint());
    LOG.info("arrayList 消耗的總時間 " + stopWatch.getTotalTimeMillis());
  }  }

然后執行單元測試后生成 如下結果:

Java JMH 基準測試工具

 

很明顯 對于add方法來講,ArrayList 的性能要比 LinkedList 的性能要好點。

在這里只是一個粗糙的測試方法,因為:

  • 1. 使用到的 StopWatch ,在其內部也會記錄方法的開始的納秒數,這種操作也會消耗一定的CPU時間。
    2.JVM 在運行時對 for 循環也有優化,這樣就會導致測試時間包含了一部分JVM性能優化的執行時間3.前后運行的 JVM 環境并不完全相同

所以為了能更嚴謹的來進行測試, JMH 就出現了。

2 JMH 基本使用

2.1 集成

JMH是 JDK9自帶的,如果你是 JDK9 之前的版本也可以通過導入 openjdk

<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>

2.2 使用 JMH 進行測試

///代碼清單 2-1
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.ArrayList;import java.util.LinkedList;import java.util.List;import java.util.concurrent.TimeUnit;//Mode 表示 JMH 進行 Benchmark 時所使用的模式
//BenchmarkMode的value是一個數組,可以把幾種Mode集合在一起執行,還可以設置為Mode.Al
@BenchmarkMode(Mode.AverageTime)
//benchmark 結果所使用的時間單位
//使用java.util.concurrent.TimeUnit中的標準時間單位
// 微秒
@OutputTimeUnit(TimeUnit.MICROSECONDS)
///JMH測試類必須使用@State注解,
// State定義了一個類實例的生命周期,
// 可以類比Spring Bean的Scope
@State(Scope.Thread)
public class DemoApplicationTestJMH {
  public static void main(String[] args) throws Exception {
    String name = DemoApplicationTestJMH.class.getName();
    Options options = new OptionsBuilder()
        .include(name )
        .forks(1)
        .measurementIterations(3)
        .warmupIterations(3)
        .build();
    new Runner(options).run();
  }
  @Benchmark
  public void testArrayList() {
    List<String> arrayList = new ArrayList<>();
    for (int i = 0; i < 10000000; i++) {
      arrayList.add("測試數據");
    }
  }
  @Benchmark
  public void testLinkedList() {
    //測試 LinkedList
    List<String> linkedList = new LinkedList<>();
    for (int i = 0; i < 10000000; i++) {
      linkedList.add("測試數據");
    }
  }
}

然后運行main 方法后控制臺日志會輸出很長的日志信息,在這里是執行了testArrayList 與testLinkedList兩個方法的基準測試,每個方法都會對應一段日志信息,小編在這里將testArrayList 方法 日志信息拆分成兩段如下:

第一段包括 JVM 的啟動參數配置信息 以及 JMH 的基本配置

/Library/Java/JavaVirtualmachines/jdk1.8.0_74.jdk/Contents/Home/bin/java "-javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=65371: ... 省略路徑
IDEA.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath 
# JMH version: 1.19
# VM version: JDK 1.8.0_74, VM 25.74-b02
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_74.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=65371:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# Warmup: 3 iterations, 1 s each
# Measurement: 3 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.example.demo.DemoApplicationTestJMH.testArrayList
# Run progress: 0.00% complete, ETA 00:00:12
# Fork: 1 of 1
objc[56915]: Class JavaLaunchHelper is implemented in both /Library/Java/JavaVirtualMachines/jdk1.8.0_74.jdk/Contents/Home/jre/bin/java (0x10d1a44c0) and /Library/Java/JavaVirtualMachines/jdk1.8.0_74.jdk/Contents/Home/jre/lib/libinstrument.dylib (0x10d1e94e0). One of the two will be used. Which one is undefined.
# Warmup Iteration   1: 101153.611 us/op
# Warmup Iteration   2: 76302.787 us/op
# Warmup Iteration   3: 54296.903 us/op
Iteration   1: 57062.920 us/op
Iteration   2: 65024.286 us/op
Iteration   3: 56325.284 us/op

分析如下圖

Java JMH 基準測試工具

 


Java JMH 基準測試工具

 


Warmup 可譯為 預熱的意思,在 JMH 中,Warmup 所做 的事情就是在基準測試代碼正式執行測量(度量)前,對其進行預熱,如 JVM運行器的編譯、JIT 的優化等等
第二段 就是JMH 對 testArrayList 方法的測試輸出信息了

Result "com.example.demo.DemoApplicationTestJMH.testArrayList":
  59470.830 ±(99.9%) 87999.595 us/op [Average]
  (min, avg, max) = (56325.284, 59470.830, 65024.286), stdev = 4823.555
  CI (99.9%): [≈ 0, 147470.424] (assumes normal distribution)
Java JMH 基準測試工具

 

然后 對于 testLinkedList 方法也會有 相同類似的日志信息只不過是輸出的數據不一樣,當兩個方法執行基準測試完成后 最后會有對比信息日志如下:

Result "com.example.demo.DemoApplicationTestJMH.testLinkedList":
  206870.042 ±(99.9%) 2571061.260 us/op [Average]
  (min, avg, max) = (104680.987, 206870.042, 367640.921), stdev = 140928.543
  CI (99.9%): [≈ 0, 2777931.302] (assumes normal distribution)
# Run complete. Total time: 00:00:16
Benchmark                              Mode  Cnt       Score         Error  Units
DemoApplicationTestJMH.testArrayList   avgt    3   59470.830 ±   87999.595  us/op
DemoApplicationTestJMH.testLinkedList  avgt    3  206870.042 ± 2571061.260  us/op

2.3 對比

我期望與 代碼清單 1-1 所使用的 StopWatch 計時對比一下時時間 ,StopWatch 中輸出的是納秒,對應的是 TimeUnit.NANOSECONDS ,所以我需要將 @OutputTimeUnit 配置的單位修改,以使用 JMH 度量后的時間單位輸出為納秒

@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class DemoApplicationTestJMH { ...}

再次運行度量測試 最終日志如下:

# Run complete. Total time: 00:00:16
Benchmark                              Mode  Cnt          Score           Error  Units
DemoApplicationTestJMH.testArrayList   avgt    3   57019569.485 ±  88169595.613  ns/op
DemoApplicationTestJMH.testLinkedList  avgt    3  368470033.556 ± 535285211.800  ns/op
Process finished with exit code 0

代碼清單 1-1 所使用的 StopWatch 日志如下:

---------------------------------------------
ns         %     Task name
---------------------------------------------
177413718  044%  arrayList 測試
227298885  056%  linkedList 測試

3 參數概述

3.1 @BenchmarkMode

對應Mode選項,可用于類或者方法上, 需要注意的是,這個注解的value是一個數組,可以把幾種Mode集合在一起執行,還可以設置為Mode.All,即全部執行一遍

Mode 表示 JMH 進行 Benchmark 時所使用的模式。通常是測量的維度不同,或是測量的方式不同。目前 JMH 共有四種模式:

  • Throughput: 整體吞吐量,例如“1秒內可以執行多少次調用”。
  • AverageTime: 調用的平均時間,例如“每次調用平均耗時xxx毫秒”。
  • SampleTime: 隨機取樣,最后輸出取樣結果的分布,例如“99%的調用在xxx毫秒以內,99.99%的調用在xxx毫秒以內”
  • SingleShotTime: 以上模式都是默認一次 iteration 是 1s,唯有 SingleShotTime 是只運行一次。往往同時把 warmup 次數設為0,用于測試冷啟動時的性能。

3.2 Iteration 與 Warmup

  
  Iteration 是 JMH 進行測試的最小單位。在大部分模式下,一次 iteration 代表的是一秒,JMH 會在這一秒內不斷調用需要 benchmark 的方法,然后根據模式對其采樣,計算吞吐量,計算平均執行時間等。

  Warmup 是指在實際進行 benchmark 前先進行預熱的行為。為什么需要預熱?因為 JVM 的 JIT 機制的存在,如果某個函數被調用多次之后,JVM 會嘗試將其編譯成為機器碼從而提高執行速度。為了讓 benchmark 的結果更加接近真實情況就需要進行預熱。

3.3 @State

類注解,JMH測試類必須使用@State注解,State定義了一個類實例的生命周期,可以類比Spring Bean的Scope。

由于JMH允許多線程同時執行測試,不同的選項含義如下:

  • Scope.Thread:默認的State,每個測試線程分配一個實例;
  • Scope.Benchmark:所有測試線程共享一個實例,用于測試有狀態實例在多線程共享下的性能;
  • Scope.Group:每個線程組共享一個實例; ##### 3.4 @OutputTimeUnit 用來配置benchmark 結果所使用的時間單位,可用于類或者方法注解,使用java.util.concurrent.TimeUnit中的標準時間單位。
TimeUnit.DAYS          //天  
TimeUnit.HOURS         //小時  
TimeUnit.MINUTES       //分鐘  
TimeUnit.SECONDS       //秒  
TimeUnit.MILLISECONDS  //毫秒 
TimeUnit.NANOSECONDS   //毫微秒 納秒
TimeUnit.MICROSECONDS  //微秒

3.5 其他

  @Benchmark

    方法注解,表示該方法是需要進行 benchmark 的對象。

  @Setup

    方法注解,會在執行 benchmark 之前被執行,正如其名,主要用于初始化。

  @TearDown

    方法注解,與@Setup 相對的,會在所有 benchmark 執行結束以后執行,主要用于資源的回收等。

  @Param

    成員注解,可以用來指定某項參數的多種情況。特別適合用來測試一個函數在不同的參數輸入的情況下的性能。@Param注解接收一個String數組,在@setup方法執行前轉化為為對應的數據類型。多個@Param注解的成員之間是乘積關系,譬如有兩個用@Param注解的字段,第一個有5個值,第二個字段有2個值,那么每個測試方法會跑5*2=10次。

分享到:
標簽:Java JMH
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定