1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Java測(cè)試,你真的搞懂了嗎?

        共 1726字,需瀏覽 4分鐘

         ·

        2022-02-13 10:56

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

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

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

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

        Java的基準(zhǔn)測(cè)試需要注意的幾個(gè)點(diǎn):

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

        JMH的使用場(chǎng)景:

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

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

        DEMO 演示

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

        1. 測(cè)試項(xiàng)目構(gòu)建

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

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

        第一種是使用命令行構(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

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

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

        <dependency>
        ????<groupId>org.openjdk.jmhgroupId>
        ????<artifactId>jmh-coreartifactId>
        ????<version>${jmh.version}version>
        dependency>
        <dependency>
        ????<groupId>org.openjdk.jmhgroupId>
        ????<artifactId>jmh-generator-annprocessartifactId>
        ????<version>${jmh.version}version>
        ????<scope>providedscope>
        dependency>

        2. 編寫性能測(cè)試

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

        /**
        ?*?@author?Richard_yyf
        ?*?@version?1.0?2019/8/27
        ?*/


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

        ????private?List?list?=?new?LinkedList<>();

        ????@Setup
        ????public?void?setUp()?{
        ????????for?(int?i?=?0;?i?????????????list.add(String.valueOf(i));
        ????????}
        ????}

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

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

        3. 執(zhí)行測(cè)試

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

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

        具體命令如下

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

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

        啟動(dòng)方式如下:

        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. 報(bào)告結(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

        整個(gè)過程:

        #?Detecting?actual?CPU?count:?12?detected
        #?JMH?version:?1.21
        #?VM?version:?JDK?1.8.0_131,?Java?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???1:?1189.267?ops/s
        #?Warmup?Iteration???2:?1197.321?ops/s
        Iteration???1:?1193.062?ops/s
        Iteration???2:?1191.698?ops/s

        Result?"org.sample.jmh.LinkedListIterationBenchMark.forEachIterate":
        ??1192.380?ops/s

        #?JMH?version:?1.21
        #?VM?version:?JDK?1.8.0_131,?Java?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???1:?205.676?ops/s
        #?Warmup?Iteration???2:?206.512?ops/s
        Iteration???1:?206.542?ops/s
        Iteration???2:?207.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

        注解介紹

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

        @BenchmarkMode

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

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

        可以注釋在方法級(jí)別,也可以注釋在類級(jí)別。

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

        @Warmup

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

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

        @Measurement

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

        • iterations?進(jìn)行測(cè)試的輪次
        • time?每輪進(jìn)行的時(shí)長(zhǎng)
        • timeUnit時(shí)長(zhǎng)單位
        @Benchmark
        @BenchmarkMode({Mode.Throughput,?Mode.SingleShotTime})
        @Measurement(iterations?=?3)
        public?void?m()?{
        ?...
        }

        @Threads

        每個(gè)進(jìn)程中的測(cè)試線程。

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

        @Fork

        進(jìn)行 fork 的次數(shù)。如果 fork 數(shù)是3的話,則 JMH 會(huì) fork 出3個(gè)進(jìn)程來進(jìn)行測(cè)試。

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

        @OutputTimeUnit

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

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

        @Benchmark

        方法級(jí)注解,表示該方法是需要進(jìn)行 benchmark 的對(duì)象,用法和 JUnit 的?@Test?類似。

        @Param

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

        @Setup

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

        @TearDown

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

        @State

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

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

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

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

        啟動(dòng)方法

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

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

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

        ????new?Runner(opt).run();
        }

        結(jié)語

        基于JMH可以對(duì)很多工具和框架進(jìn)行測(cè)試,比如日志框架性能對(duì)比、BeanCopy性能對(duì)比 等,更多的example可以參考官方給出的JMH samples(https://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/)

        來源:juejin.cn/post/6844903936869007368

        ——————END——————

        歡迎關(guān)注“Java引導(dǎo)者”,我們分享最有價(jià)值的Java的干貨文章,助力您成為有思想的Java開發(fā)工程師!

        瀏覽 43
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲日韩一区二区在线观看 | chinese勾搭少妇videos | 国产成人手机视频 | 女人的被男人桶爽网站 | 一本道大香蕉人人操 | 黄色色色色网站 | 91麻豆精品国产91久久久熟女 | 18岁禁黄网站 | 免费黄色毛片 | 国产精品男女 |