成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

性能調優必備利器之JMH

開發 后端
本文主要介紹了性能基準測試工具 JMH,它可以通過一些功能來規避由 JVM 中的 JIT 或者其他優化對性能測試造成的影響。

if 快還是 switch 快?HashMap 的初始化 size 要不要指定,指定之后性能可以提高多少?各種序列化方法哪個耗時更短?

無論出自何種原因需要進行性能評估,量化指標總是必要的。

在大部分場合,簡單地回答誰快誰慢是遠遠不夠的,如何將程序性能量化呢?

這就需要我們的主角 JMH 登場了!

JMH 簡介

JMH(Java Microbenchmark Harness)是用于代碼微基準測試的工具套件,主要是基于方法層面的基準測試,精度可以達到納秒級。該工具是由 Oracle 內部實現 JIT 的大牛們編寫的,他們應該比任何人都了解 JIT 以及 JVM 對于基準測試的影響。

當你定位到熱點方法,希望進一步優化方法性能的時候,就可以使用 JMH 對優化的結果進行量化的分析。

JMH 比較典型的應用場景如下:

  1.  想準確地知道某個方法需要執行多長時間,以及執行時間和輸入之間的相關性
  2.  對比接口不同實現在給定條件下的吞吐量
  3.  查看多少百分比的請求在多長時間內完成

下面我們以字符串拼接的兩種方法為例子使用 JMH 做基準測試。

加入依賴

因為 JMH 是 JDK9 自帶的,如果是 JDK9 之前的版本需要加入如下依賴(目前 JMH 的最新版本為 1.23): 

  1. <dependency>  
  2.     <groupId>org.openjdk.jmh</groupId>  
  3.     <artifactId>jmh-core</artifactId>  
  4.     <version>1.23</version>  
  5. </dependency>  
  6. <dependency>  
  7.     <groupId>org.openjdk.jmh</groupId>  
  8.     <artifactId>jmh-generator-annprocess</artifactId>  
  9.     <version>1.23</version>  
  10. </dependency> 

編寫基準測試

接下來,創建一個 JMH 測試類,用來判斷 + 和 StringBuilder.append() 兩種字符串拼接哪個耗時更短,具體代碼如下所示: 

  1. @BenchmarkMode(Mode.AverageTime)  
  2. @Warmup(iterations = 3time = 1 
  3. @Measurement(iterations = 5time = 5 
  4. @Threads(4)  
  5. @Fork(1)  
  6. @State(value = Scope.Benchmark)  
  7. @OutputTimeUnit(TimeUnit.NANOSECONDS)  
  8. public class StringConnectTest {  
  9.     @Param(value = {"10", "50", "100"})  
  10.     private int length;  
  11.     @Benchmark  
  12.     public void testStringAdd(Blackhole blackhole) {  
  13.         String a = "" 
  14.         for (int i = 0; i < length; i++) {  
  15.             a += i;  
  16.         }  
  17.         blackhole.consume(a);  
  18.     }  
  19.     @Benchmark  
  20.     public void testStringBuilderAdd(Blackhole blackhole) {  
  21.         StringBuilder sb = new StringBuilder();  
  22.         for (int i = 0; i < length; i++) {  
  23.             sb.append(i);  
  24.         }  
  25.         blackhole.consume(sb.toString());  
  26.     }  
  27.     public static void main(String[] args) throws RunnerException {  
  28.         Options opt = new OptionsBuilder()  
  29.                 .include(StringConnectTest.class.getSimpleName())  
  30.                 .result("result.json")  
  31.                 .resultFormat(ResultFormatType.JSON).build();  
  32.         new Runner(opt).run();  
  33.     }  

其中需要測試的方法用 @Benchmark 注解標識,這些注解的具體含義將在下面介紹。

在 main() 函數中,首先對測試用例進行配置,使用 Builder 模式配置測試,將配置參數存入 Options 對象,并使用 Options 對象構造 Runner 啟動測試。

另外大家可以看下官方提供的 jmh 示例 demo:s/src/main/java/org/openjdk/jmh/samples/" _fcksavedurl="http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/">http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/

執行基準測試

準備工作做好了,接下來,運行代碼,等待片刻,測試結果就出來了,下面對結果做下簡單說明: 

  1. # JMH version: 1.23  
  2. # VM version: JDK 1.8.0_201, Java HotSpot(TM) 64-Bit Server VM, 25.201-b09  
  3. # VM invoker: D:\Software\Java\jdk1.8.0_201\jre\bin\java.exe  
  4. # VM options: -javaagent:D:\Software\JetBrains\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar=61018:D:\Software\JetBrains\IntelliJ IDEA 2019.1.3\bin -Dfile.encoding=UTF-8 
  5. # Warmup: 3 iterations, 1 s each  
  6. # Measurement: 5 iterations, 5 s each  
  7. # Timeout: 10 min per iteration  
  8. # Threads: 4 threads, will synchronize iterations  
  9. # Benchmark mode: Average time, time/op  
  10. # Benchmark: com.wupx.jmh.StringConnectTest.testStringBuilderAdd  
  11. # Parameters: (length = 100

該部分為測試的基本信息,比如使用的 Java 路徑,預熱代碼的迭代次數,測量代碼的迭代次數,使用的線程數量,測試的統計單位等。 

  1. # Warmup Iteration   1: 1083.569 ±(99.9%) 393.884 ns/op  
  2. # Warmup Iteration   2: 864.685 ±(99.9%) 174.120 ns/op  
  3. # Warmup Iteration   3: 798.310 ±(99.9%) 121.161 ns/op 

該部分為每一次熱身中的性能指標,預熱測試不會作為最終的統計結果。預熱的目的是讓 JVM 對被測代碼進行足夠多的優化,比如,在預熱后,被測代碼應該得到了充分的 JIT 編譯和優化。 

  1. Iteration   1: 810.667 ±(99.9%) 51.505 ns/op  
  2. Iteration   2: 807.861 ±(99.9%) 13.163 ns/op  
  3. Iteration   3: 851.421 ±(99.9%) 33.564 ns/op  
  4. Iteration   4: 805.675 ±(99.9%) 33.038 ns/op  
  5. Iteration   5: 821.020 ±(99.9%) 66.943 ns/op  
  6. Result "com.wupx.jmh.StringConnectTest.testStringBuilderAdd":  
  7.   819.329 ±(99.9%) 72.698 ns/op [Average]  
  8.   (min, avg, max) = (805.675, 819.329, 851.421), stdev = 18.879  
  9.   CI (99.9%): [746.631, 892.027] (assumes normal distribution)  
  10. Benchmark                               (length)  Mode  Cnt     Score     Error  Units  
  11. StringConnectTest.testStringBuilderAdd       100  avgt    5   819.329 ±  72.698  ns/op 

該部分顯示測量迭代的情況,每一次迭代都顯示了當前的執行速率,即一個操作所花費的時間。在進行 5 次迭代后,進行統計,在本例中,length 為 100 的情況下 testStringBuilderAdd 方法的平均執行花費時間為 819.329 ns,誤差為 72.698 ns。

最后的測試結果如下所示: 

  1. Benchmark                               (length)  Mode  Cnt     Score     Error  Units  
  2. StringConnectTest.testStringAdd               10  avgt    5   161.496 ±  17.097  ns/op  
  3. StringConnectTest.testStringAdd               50  avgt    5  1854.657 ± 227.902  ns/op  
  4. StringConnectTest.testStringAdd              100  avgt    5  6490.062 ± 327.626  ns/op  
  5. StringConnectTest.testStringBuilderAdd        10  avgt    5    68.769 ±   4.460  ns/op  
  6. StringConnectTest.testStringBuilderAdd        50  avgt    5   413.021 ±  30.950  ns/op  
  7. StringConnectTest.testStringBuilderAdd       100  avgt    5   819.329 ±  72.698  ns/op 

結果表明,在拼接字符次數越多的情況下,StringBuilder.append() 的性能就更好。

生成 jar 包執行

對于一些小測試,直接用上面的方式寫一個 main 函數手動執行就好了。

對于大型的測試,需要測試的時間比較久、線程數比較多,加上測試的服務器需要,一般要放在 Linux 服務器里去執行。

JMH 官方提供了生成 jar 包的方式來執行,我們需要在 maven 里增加一個 plugin,具體配置如下: 

  1. <plugins>  
  2.     <plugin>  
  3.         <groupId>org.apache.maven.plugins</groupId>  
  4.         <artifactId>maven-shade-plugin</artifactId>  
  5.         <version>2.4.1</version>  
  6.         <executions>  
  7.             <execution>  
  8.                 <phase>package</phase>  
  9.                 <goals>  
  10.                     <goal>shade</goal>  
  11.                 </goals>  
  12.                 <configuration>  
  13.                     <finalName>jmh-demo</finalName>  
  14.                     <transformers> 
  15.                          <transformer  
  16.                                 implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">  
  17.                             <mainClass>org.openjdk.jmh.Main</mainClass>  
  18.                         </transformer>  
  19.                     </transformers>  
  20.                 </configuration>  
  21.             </execution>  
  22.         </executions>  
  23.     </plugin>  
  24. </plugins> 

接著執行 maven 的命令生成可執行 jar 包并執行: 

  1. mvn clean install  
  2. java -jar target/jmh-demo.jar StringConnectTest 

JMH 基礎

為了能夠更好地使用 JMH 的各項功能,下面對 JMH 的基本概念進行講解:

@BenchmarkMode

用來配置 Mode 選項,可用于類或者方法上,這個注解的 value 是一個數組,可以把幾種 Mode 集合在一起執行,如:@BenchmarkMode({Mode.SampleTime, Mode.AverageTime}),還可以設置為 Mode.All,即全部執行一遍。

  1.  Throughput:整體吞吐量,每秒執行了多少次調用,單位為 ops/time
  2.  AverageTime:用的平均時間,每次操作的平均時間,單位為 time/op
  3.  SampleTime:隨機取樣,最后輸出取樣結果的分布
  4.  SingleShotTime:只運行一次,往往同時把 Warmup 次數設為 0,用于測試冷啟動時的性能
  5.  All:上面的所有模式都執行一次

@State

通過 State 可以指定一個對象的作用范圍,JMH 根據 scope 來進行實例化和共享操作。@State 可以被繼承使用,如果父類定義了該注解,子類則無需定義。由于 JMH 允許多線程同時執行測試,不同的選項含義如下:

  1.  Scope.Benchmark:所有測試線程共享一個實例,測試有狀態實例在多線程共享下的性能
  2.  Scope.Group:同一個線程在同一個 group 里共享實例
  3.  Scope.Thread:默認的 State,每個測試線程分配一個實例

@OutputTimeUnit

為統計結果的時間單位,可用于類或者方法注解

@Warmup

預熱所需要配置的一些基本測試參數,可用于類或者方法上。一般前幾次進行程序測試的時候都會比較慢,所以要讓程序進行幾輪預熱,保證測試的準確性。參數如下所示:

  1.  iterations:預熱的次數
  2.  time:每次預熱的時間
  3.  timeUnit:時間的單位,默認秒
  4.  batchSize:批處理大小,每次操作調用幾次方法

為什么需要預熱?

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

@Measurement

實際調用方法所需要配置的一些基本測試參數,可用于類或者方法上,參數和 @Warmup 相同。

@Threads

每個進程中的測試線程,可用于類或者方法上。

@Fork

進行 fork 的次數,可用于類或者方法上。如果 fork 數是 2 的話,則 JMH 會 fork 出兩個進程來進行測試。

@Param

指定某項參數的多種情況,特別適合用來測試一個函數在不同的參數輸入的情況下的性能,只能作用在字段上,使用該注解必須定義 @State 注解。

在介紹完常用的注解后,讓我們來看下 JMH 有哪些陷阱。

JMH 陷阱

在使用 JMH 的過程中,一定要避免一些陷阱。

比如 JIT 優化中的死碼消除,比如以下代碼: 

  1. @Benchmark  
  2. public void testStringAdd(Blackhole blackhole) {  
  3.     String a = "" 
  4.     for (int i = 0; i < length; i++) {  
  5.         a += i;  
  6.     }  

JVM 可能會認為變量 a 從來沒有使用過,從而進行優化把整個方法內部代碼移除掉,這就會影響測試結果。

JMH 提供了兩種方式避免這種問題,一種是將這個變量作為方法返回值 return a,一種是通過 Blackhole 的 consume 來避免 JIT 的優化消除。

其他陷阱還有常量折疊與常量傳播、永遠不要在測試中寫循環、使用 Fork 隔離多個測試方法、方法內聯、偽共享與緩存行、分支預測、多線程測試等,感興趣的可以閱讀 https://github.com/lexburner/JMH-samples 了解全部的陷阱。

JMH 插件

大家還可以通過 IDEA 安裝 JMH 插件使 JMH 更容易實現基準測試,在 IDEA 中點擊 File->Settings...->Plugins,然后搜索 jmh,選擇安裝 JMH plugin:

JMH plugin

這個插件可以讓我們能夠以 JUnit 相同的方式使用 JMH,主要功能如下:

  1.  自動生成帶有 @Benchmark 的方法
  2.  像 JUnit 一樣,運行單獨的 Benchmark 方法
  3.  運行類中所有的 Benchmark 方法

比如可以通過右鍵點擊 Generate...,選擇操作 Generate JMH benchmark 就可以生成一個帶有 @Benchmark 的方法。

還有將光標移動到方法聲明并調用 Run 操作就運行一個單獨的 Benchmark 方法。

將光標移到類名所在行,右鍵點擊 Run 運行,該類下的所有被 @Benchmark 注解的方法都會被執行。

JMH 可視化

除此以外,如果你想將測試結果以圖表的形式可視化,可以試下這些網站:

  •  JMH Visual Chart:http://deepoove.com/jmh-visual-chart/
  •  JMH Visualizer:https://jmh.morethan.io/

比如將上面測試例子結果的 json 文件導入,就可以實現可視化:

總結

本文主要介紹了性能基準測試工具 JMH,它可以通過一些功能來規避由 JVM 中的 JIT 或者其他優化對性能測試造成的影響。

只需要將待測的業務邏輯用 @Benchmark 注解標識,就可以讓 JMH 的注解處理器自動生成真正的性能測試代碼,以及相應的性能測試配置文件。 

 

責任編輯:龐桂玉 來源: Java編程
相關推薦

2017-07-21 08:55:13

TomcatJVM容器

2013-03-20 17:18:07

Linux系統性能調優

2011-03-18 11:13:07

LAMP度量性能

2011-03-21 09:35:38

LAMP調優網絡文件

2021-07-15 08:00:47

系統性能調優cpunuma架構

2023-11-28 08:43:48

2012-06-20 11:05:47

性能調優攻略

2021-03-04 08:39:21

SparkRDD調優

2020-01-14 09:13:48

Tomcat調優配置

2011-03-21 09:17:35

LAMP調優磁盤

2011-03-10 14:40:54

LAMPMysql

2011-05-20 15:02:01

Oracle性能調優

2020-11-30 11:40:35

NginxLinux性能調優

2011-11-14 10:28:23

2011-02-15 09:31:56

ASP.NET

2011-03-18 11:21:48

2013-02-28 10:15:14

Ubuntu性能調優故障排查

2012-06-21 09:43:45

2024-12-04 15:49:29

2021-11-07 23:49:19

SQL數據庫工具
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品视频一区二区三区 | 国产农村妇女毛片精品久久麻豆 | 精品一区二区在线观看 | 久久伊人一区二区 | 欧美成人精品激情在线观看 | 一区视频 | 狠狠久| 久久久久国产一区二区三区四区 | 草久久 | 91视频一区二区三区 | 夜夜草| 久久国产精品色av免费观看 | 三区四区在线观看 | 日韩精品久久一区 | 欧美aa在线| 午夜在线视频一区二区三区 | 亚洲国产成人在线视频 | 国产一区二区三区在线看 | 中国美女撒尿txxxxx视频 | 久久av一区二区 | 国产精品久久av | 91视频国产一区 | 久久精品欧美一区二区三区麻豆 | 黄色片免费在线观看 | 欧美高清性xxxxhd| 青青草网 | 亚洲日本中文字幕在线 | 欧美一区2区三区3区公司 | 国产yw851.c免费观看网站 | 在线视频一区二区 | 国产精品区一区二区三 | 玖玖国产| 日本成人综合 | 欧美日韩在线精品 | 久久精品a| 亚洲精品中文字幕在线 | h视频在线观看免费 | 视频一二区 | 美女黄色在线观看 | 欧美精品1区| 亚洲欧美视频 |