<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          太 Low!這才是專業(yè) Java 測試方法!

          共 15705字,需瀏覽 32分鐘

           ·

          2022-07-05 12:18

          不點藍字關(guān)注,我們哪來故事?


          在日常開發(fā)中,我們對一些代碼的調(diào)用或者工具的使用會存在多種選擇方式,在不確定他們性能的時候,我們首先想要做的就是去測量它。大多數(shù)時候,我們會簡單的采用多次計數(shù)的方式來測量,來看這個方法的總耗時。

          但是,如果熟悉JVM類加載機制的話,應(yīng)該知道JVM默認(rèn)的執(zhí)行模式是JIT編譯與解釋混合執(zhí)行。JVM通過熱點代碼統(tǒng)計分析,識別高頻方法的調(diào)用、循環(huán)體、公共模塊等,基于JIT動態(tài)編譯技術(shù),會將熱點代碼轉(zhuǎn)換成機器碼,直接交給CPU執(zhí)行。

           

          也就是說,JVM會不斷的進行編譯優(yōu)化,這就使得很難確定重復(fù)多少次才能得到一個穩(wěn)定的測試結(jié)果?所以,很多有經(jīng)驗的同學(xué)會在測試代碼前寫一段預(yù)熱的邏輯。

          JMH,全稱 Java Microbenchmark Harness (微基準(zhǔn)測試框架),是專門用于Java代碼微基準(zhǔn)測試的一套測試工具API,是由 OpenJDK/Oracle 官方發(fā)布的工具。何謂 Micro Benchmark 呢?簡單地說就是在 method 層面上的 benchmark,精度可以精確到微秒級。

          注 意
           文末有:7701頁互聯(lián)網(wǎng)大廠面試題 

          Java的基準(zhǔn)測試需要注意的幾個點:

          • 測試前需要預(yù)熱。
          • 防止無用代碼進入測試方法中。
          • 并發(fā)測試。
          • 測試結(jié)果呈現(xiàn)。

          JMH的使用場景:

          1、 定量分析某個熱點函數(shù)的優(yōu)化效果;
          2、 想定量地知道某個函數(shù)需要執(zhí)行多長時間,以及執(zhí)行時間和輸入變量的相關(guān)性;
          3、 對比一個函數(shù)的多種實現(xiàn)方式;

          本篇主要是介紹JMH的DEMO演示,和常用的注解參數(shù)。希望能對你起到幫助。

          DEMO 演示

          這里先演示一個DEMO,讓不了解JMH的同學(xué)能夠快速掌握這個工具的大概用法。

          1. 測試項目構(gòu)建

          JMH是內(nèi)置Java9及之后的版本。這里是以Java8進行說明。

          為了方便,這里直接介紹使用maven構(gòu)建JMH測試項目的方式。

          第一種是使用命令行構(gòu)建,在指定目錄下執(zhí)行以下命令:

          $ mvn archetype:generate \
                    -DinteractiveMode=false \
                    -DarchetypeGroupId=org.openjdk.jmh \
                    -DarchetypeArtifactId=jmh-java-benchmark-archetype \
                    -DgroupId=org.sample \
                    -DartifactId=test \
                    -Dversion=1.0

          對應(yīng)目錄下會出現(xiàn)一個test項目,打開項目后我們會看到這樣的項目結(jié)構(gòu)。

           

          第二種方式就是直接在現(xiàn)有的maven項目中添加jmh-corejmh-generator-annprocess的依賴來集成JMH。

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

          2. 編寫性能測試

          這里我以測試LinkedList 通過index 方式迭代和foreach 方式迭代的性能差距為例子,編寫測試類,涉及到的注解在之后會講解,

          /**
           * @author Richard_yyf
           */


          @State(Scope.Benchmark)
          @OutputTimeUnit(TimeUnit.SECONDS)
          @Threads(Threads.MAX)
          public class LinkedListIterationBenchMark {
           private static final int SIZE = 10000;

              private List<String> list = new LinkedList<>();
              
              @Setup
              public void setUp() {
                  for (int i = 0; i < SIZE; i++) {
                      list.add(String.valueOf(i));
                  }
              }

              @Benchmark
              @BenchmarkMode(Mode.Throughput)
              public void forIndexIterate() {
                  for (int i = 0; i < list.size(); i++) {
                      list.get(i);
                      System.out.print("");
                  }
              }

              @Benchmark
              @BenchmarkMode(Mode.Throughput)
              public void forEachIterate() {
                  for (String s : list) {
                      System.out.print("");
                  }
              }
          }

          3. 執(zhí)行測試

          運行 JMH 基準(zhǔn)測試有兩種方式,一個是生產(chǎn)jar文件運行,另一個是直接寫main函數(shù)或者放在單元測試中執(zhí)行。

          生成jar文件的形式主要是針對一些比較大的測試,可能對機器性能或者真實環(huán)境模擬有一些需求,需要將測試方法寫好了放在linux環(huán)境執(zhí)行。具體命令如下

          $ mvn clean install
          $ java -jar target/benchmarks.jar

          我們?nèi)粘V杏龅降囊话闶且恍┬y試,比如我上面寫的例子,直接在IDE中跑就好了。啟動方式如下:

           public static void main(String[] args) throws RunnerException {
                  Options opt = new OptionsBuilder()
                          .include(LinkedListIterationBenchMark.class.getSimpleName())
                          .forks(1)
                          .warmupIterations(2)
                          .measurementIterations(2)
                       .output("E:/Benchmark.log")
                          .build()
          ;

                  new Runner(opt).run();
              }

          4. 報告結(jié)果

          輸出結(jié)果如下,

          最后的結(jié)果:

          Benchmark                                      Mode  Cnt     Score   Error  Units
          LinkedListIterationBenchMark.forEachIterate   thrpt    2  1192.380          ops/s
          LinkedListIterationBenchMark.forIndexIterate  thrpt    2   206.866          ops/s

          整個過程:

          # Detecting actual CPU count: 12 detected
          # JMH version: 1.21
          # VM version: JDK 1.8.0_131Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
          # VM invoker: C:\Program Files\Java\jdk1.8.0_131\jre\bin\java.exe
          # VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar
          =65175:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\bin -Dfile.encoding=UTF-8
          # Warmup: 2 iterations, 10 s each
          # Measurement: 2 iterations, 10 s each
          # Timeout: 10 min per iteration
          # Threads: 12 threads, will synchronize iterations
          # Benchmark mode: Throughput, ops/time
          # Benchmark: org.sample.jmh.LinkedListIterationBenchMark.forEachIterate

          # Run progress: 0.00% complete, ETA 00:01:20
          # Fork: 1 of 1
          # Warmup Iteration   11189.267 ops/s
          # Warmup Iteration   21197.321 ops/s
          Iteration   11193.062 ops/s
          Iteration   21191.698 ops/s
          Result "org.sample.jmh.LinkedListIterationBenchMark.forEachIterate":
            1192.380 ops/s
          # JMH version: 1.21
          # VM version: JDK 1.8.0_131Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
          # VM invoker: C:\Program Files\Java\jdk1.8.0_131\jre\bin\java.exe
          # VM options: -javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\lib\idea_rt.jar
          =65175:D:\Program Files\JetBrains\IntelliJ IDEA 2018.2.2\bin -Dfile.encoding=UTF-8
          # Warmup: 2 iterations, 10 s each
          # Measurement: 2 iterations, 10 s each
          # Timeout: 10 min per iteration
          # Threads: 12 threads, will synchronize iterations
          # Benchmark mode: Throughput, ops/time
          # Benchmark: org.sample.jmh.LinkedListIterationBenchMark.forIndexIterate

          # Run progress: 50.00% complete, ETA 00:00:40
          # Fork: 1 of 1
          # Warmup Iteration   1205.676 ops/s
          # Warmup Iteration   2206.512 ops/s
          Iteration   1206.542 ops/s
          Iteration   2207.189 ops/s
          Result "org.sample.jmh.LinkedListIterationBenchMark.forIndexIterate":
            206.866 ops/s
          # Run complete. Total time: 00:01:21

          REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
          why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
          experiments, perform baseline and negative tests that provide experimental control, make sure
          the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
          Do not assume the numbers tell you what you want them to tell.

          Benchmark                                      Mode  Cnt     Score   Error  Units
          LinkedListIterationBenchMark.forEachIterate   thrpt    2  1192.380          ops/s
          LinkedListIterationBenchMark.forIndexIterate  thrpt    2   206.866          ops/s

          注解介紹

          下面我們來詳細介紹一下相關(guān)的注解,

          @BenchmarkMode

          微基準(zhǔn)測試類型。JMH 提供了以下幾種類型進行支持:

          類型描述
          Throughput每段時間執(zhí)行的次數(shù),一般是秒
          AverageTime平均時間,每次操作的平均耗時
          SampleTime在測試中,隨機進行采樣執(zhí)行的時間
          SingleShotTime在每次執(zhí)行中計算耗時
          All所有模式

          可以注釋在方法級別,也可以注釋在類級別,

          @BenchmarkMode(Mode.All)
          public class LinkedListIterationBenchMark {
           ...
          }
          @Benchmark
          @BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
          public void m() {
           ...
          }

          @Warmup

          這個單詞的意思就是預(yù)熱,iterations = 3就是指預(yù)熱輪數(shù)。

          @Benchmark
          @BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
          @Warmup(iterations = 3)
          public void m() {
           ...
          }

          @Measurement

          正式度量計算的輪數(shù)。

          • iterations 進行測試的輪次
          • time 每輪進行的時長
          • timeUnit時長單位
          @Benchmark
          @BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
          @Measurement(iterations = 3)
          public void m() {
           ...
          }

          @Threads

          每個進程中的測試線程。

          @Threads(Threads.MAX)
          public class LinkedListIterationBenchMark {
           ...
          }

          @Fork

          進行fork 的次數(shù)。如果 fork 數(shù)是3的話,則 JMH 會 fork 出3個進程來進行測試。

          @Benchmark
          @BenchmarkMode({Mode.Throughput, Mode.SingleShotTime})
          @Fork(value = 3)
          public void m() {
           ...
          }

          @OutputTimeUnit

          基準(zhǔn)測試結(jié)果的時間類型。一般選擇秒、毫秒、微秒。

          @OutputTimeUnit(TimeUnit.SECONDS)
          public class LinkedListIterationBenchMark {
           ...
          }

          @Benchmark

          方法級注解,表示該方法是需要進行 benchmark 的對象,用法和 JUnit 的 @Test 類似。

          @Param

          屬性級注解,@Param 可以用來指定某項參數(shù)的多種情況。特別適合用來測試一個函數(shù)在不同的參數(shù)輸入的情況下的性能。

          @Setup

          方法級注解,這個注解的作用就是我們需要在測試之前進行一些準(zhǔn)備工作,比如對一些數(shù)據(jù)的初始化之類的。

          @TearDown

          方法級注解,這個注解的作用就是我們需要在測試之后進行一些結(jié)束工作,比如關(guān)閉線程池,數(shù)據(jù)庫連接等的,主要用于資源的回收等。

          @State

          當(dāng)使用@Setup參數(shù)的時候,必須在類上加這個參數(shù),不然會提示無法運行。

          就比如我上面的例子中,就必須設(shè)置state。

          State 用于聲明某個類是一個“狀態(tài)”,然后接受一個 Scope 參數(shù)用來表示該狀態(tài)的共享范圍。因為很多 benchmark 會需要一些表示狀態(tài)的類,JMH 允許你把這些類以依賴注入的方式注入到 benchmark 函數(shù)里。Scope 主要分為三種。

          1、 Thread:該狀態(tài)為每個線程獨享;
          2、 Group:該狀態(tài)為同一個組里面所有線程共享;
          3、 Benchmark:該狀態(tài)在所有線程間共享;

          啟動方法

          在啟動方法中,可以直接指定上述說到的一些參數(shù),并且能將測試結(jié)果輸出到指定文件中,

            /**
               * 僅限于IDE中運行
               * 命令行模式 則是 build 然后 java -jar 啟動
               *
               * 1. 這是benchmark 啟動的入口
               * 2. 這里同時還完成了JMH測試的一些配置工作
               * 3. 默認(rèn)場景下,JMH會去找尋標(biāo)注了@Benchmark的方法,可以通過include和exclude兩個方法來完成包含以及排除的語義
               */

              public static void main(String[] args) throws RunnerException {
                  Options opt = new OptionsBuilder()
                          // 包含語義
                          // 可以用方法名,也可以用XXX.class.getSimpleName()
                          .include("Helloworld")
                          // 排除語義
                          .exclude("Pref")
                          // 預(yù)熱10輪
                          .warmupIterations(10)
                          // 代表正式計量測試做10輪,
                          // 而每次都是先執(zhí)行完預(yù)熱再執(zhí)行正式計量,
                          // 內(nèi)容都是調(diào)用標(biāo)注了@Benchmark的代碼。
                          .measurementIterations(10)
                          //  forks(3)指的是做3輪測試,
                          // 因為一次測試無法有效的代表結(jié)果,
                          // 所以通過3輪測試較為全面的測試,
                          // 而每一輪都是先預(yù)熱,再正式計量。
                          .forks(3)
                       .output("E:/Benchmark.log")
                          .build();

                  new Runner(opt).run();
              }

          結(jié)語

          基于JMH可以對很多工具和框架進行測試,比如日志框架性能對比、BeanCopy性能對比 等,更多的example可以參考官方給出的

          JMH samples

          https://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/

          作者從Java Developer 角度來談?wù)勔恍┏R姷拇a測試陷阱,分析他們和操作系統(tǒng)底層以及 Java 底層的關(guān)聯(lián)性,并借助 JMH 來幫助大家擺脫這些陷阱。

          來源:juejin.cn/post/6844903936869007368

          ////// END //////
          ↓ 點擊下方關(guān)注,看更多架構(gòu)分享 ↓
          瀏覽 54
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产豆花视频在 | 尹人在线大香蕉 | 国产乱伦a片视频 | 91视频免费观看 | 蜜桃精品av久久久久久 |