本文是對作者上一篇文章中 Java 面試題之 Logback 打印日志是如何獲取當前方法名稱的? 介紹的四種獲取當前執行方法名稱方案的基準測試報告。這四種方法如下,
本文將通過使用專業基準測試工具 JMH 來對如上四種方案進行測試。
基準測試,也稱之為性能測試,是一種用于衡量計算機系統,軟件應用或硬件組件性能的測試方法。基準測試旨在通過運行一系列標準化的任務場景來測量系統的性能表現,從而幫助評估系統的各種指標,如響應時間、吞吐量、延遲、資源利用率等。
JMH,即 Java Microbenchmark Harness,是專門用于代碼微基準測試的工具套件。何謂 Micro Benchmark 呢?簡單的來說就是基于方法層面的基準測試,精度可以達到微秒級。其由 Oracle/openjdk 內部開發 JIT 編譯器的大佬們所開發,作為 Java 的方法級性能測試工具可以說是非常合適的。
測試環境是作者 2018 年購買的筆記本,配置如下,
圖片
重點看:
本文不打算做基準測試工具 JMH 的詳細科普文章,有興趣的大家自行百度 JMH 使用。所以我在這里只給大家講解 JMH 的相關概念以及下文會用到的常用注解。
JMH 可以通過注解和配置參數來控制測試的環境和結果,例如預熱次數,迭代次數,線程數,時間單位等。它還可以生成詳細的測試報告,包括最小值,平均值,最大值,標準差,置信區間等。
Throughput:整體吞吐量,例如“1 秒內可以執行多少次調用”。
AverageTime:調用的平均時間,例如“每次調用平均耗時 xxx 毫秒”。
SampleTime:隨機取樣,最后輸出取樣結果的分布,例如“99%的調用在 xxx 毫秒以內,99.99%的調用在 xxx 毫秒以內”。
SingleShotTime:單次調用時間,適合用于冷啟動測試,只運行一次,可以設置@Warmup(iterations = 0)來禁用預熱。
All:上述所有模式的綜合。
基準測試參數配置,
Warmup: 3 iterations, 10 s eachMeasurement: 5 iterations, 3 s eachTimeout: 10 min per iterationThreads: 8 threads, will synchronize iterationsBenchmark mode: Throughput
測試代碼,
需要說明的是下面的四種測試方法的 JMH 注解配置以及 main 方法都是相同的。所以為了節約篇幅,突出重點,后面三種方案將省去 JMH 注解以及 main 方法配置。
@BenchmarkMode(Mode.Throughput)@Warmup(iterations = 3)@Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS)@Threads(8)@Fork(2)@OutputTimeUnit(TimeUnit.MILLISECONDS)public class MethodNameTest { @Benchmark @BenchmarkMode({Mode.Throughput}) public void m1() { // 獲取當前方法名 String methodName = Thread.currentThread().getStackTrace()[1].getMethodName(); } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(MethodNameTest.class.getSimpleName()) .build(); new Runner(opt).run(); }}
測試結果,
...# Run progress: 0.00% complete, ETA 00:06:00# Fork: 1 of 2# Warmup Iteration 1: 16.946 ops/ms# Warmup Iteration 2: 17.086 ops/ms# Warmup Iteration 3: 17.116 ops/msIteration 1: 17.159 ops/msIteration 2: 17.118 ops/msIteration 3: 17.279 ops/msIteration 4: 17.329 ops/msIteration 5: 17.241 ops/ms# Run progress: 12.50% complete, ETA 00:05:23# Fork: 2 of 2# Warmup Iteration 1: 16.546 ops/ms# Warmup Iteration 2: 17.340 ops/ms# Warmup Iteration 3: 17.431 ops/msIteration 1: 17.331 ops/msIteration 2: 17.099 ops/msIteration 3: 17.280 ops/msIteration 4: 17.511 ops/msIteration 5: 17.323 ops/msResult "ltd.newbee.mall.MethodNameTest.m1": 17.267 ±(99.9%) 0.184 ops/ms [Average] (min, avg, max) = (17.099, 17.267, 17.511), stdev = 0.122 CI (99.9%): [17.083, 17.451] (assumes normal distribution)
上面一大堆輸出信息,大家直接看重點,在最后 Result "ltd.newbee.mall.MethodNameTest.m1": 這里,平均 ops 是每毫秒 17 次,比較低。
測試代碼,
@Benchmark@BenchmarkMode({Mode.Throughput})public void m2() { // 獲取當前方法名 String methodName = new Throwable().getStackTrace()[0].getMethodName();}
測試結果,
...# Run progress: 25.00% complete, ETA 00:04:37# Fork: 1 of 2# Warmup Iteration 1: 12.891 ops/ms# Warmup Iteration 2: 12.873 ops/ms# Warmup Iteration 3: 13.023 ops/msIteration 1: 25.617 ops/msIteration 2: 25.840 ops/msIteration 3: 25.301 ops/msIteration 4: 24.839 ops/msIteration 5: 25.930 ops/ms# Run progress: 37.49% complete, ETA 00:03:51# Fork: 2 of 2# Warmup Iteration 1: 12.511 ops/ms# Warmup Iteration 2: 12.329 ops/ms# Warmup Iteration 3: 13.011 ops/msIteration 1: 23.842 ops/msIteration 2: 24.292 ops/msIteration 3: 25.600 ops/msIteration 4: 25.745 ops/msIteration 5: 25.789 ops/msResult "ltd.newbee.mall.MethodNameTest.m2": 25.280 ±(99.9%) 1.088 ops/ms [Average] (min, avg, max) = (23.842, 25.280, 25.930), stdev = 0.720 CI (99.9%): [24.191, 26.368] (assumes normal distribution)
直接看最后 Result "ltd.newbee.mall.MethodNameTest.m2": 這里,平均 ops 是每毫秒 25 次,也比較低。
測試代碼,
@Benchmark@BenchmarkMode({Mode.Throughput})public void m1() { // 獲取當前方法名 String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();}
測試結果,
...# Run progress: 49.99% complete, ETA 00:03:04# Fork: 1 of 2# Warmup Iteration 1: 10489.110 ops/ms# Warmup Iteration 2: 9233.590 ops/ms# Warmup Iteration 3: 10504.615 ops/msIteration 1: 10695.898 ops/msIteration 2: 10570.155 ops/msIteration 3: 11089.810 ops/msIteration 4: 10805.448 ops/msIteration 5: 10027.222 ops/ms# Run progress: 62.49% complete, ETA 00:02:18# Fork: 2 of 2# Warmup Iteration 1: 11322.008 ops/ms# Warmup Iteration 2: 10025.593 ops/ms# Warmup Iteration 3: 10808.095 ops/msIteration 1: 10684.594 ops/msIteration 2: 11241.540 ops/msIteration 3: 10742.348 ops/msIteration 4: 9940.437 ops/msIteration 5: 11226.023 ops/msResult "ltd.newbee.mall.MethodNameTest.m3": 10702.347 ±(99.9%) 672.631 ops/ms [Average] (min, avg, max) = (9940.437, 10702.347, 11241.540), stdev = 444.904 CI (99.9%): [10029.716, 11374.979] (assumes normal distribution)
直接看最后 Result "ltd.newbee.mall.MethodNameTest.m3": 這里,平均 ops 是每毫秒 10702 次。非常夸張,可以看到 ops 對比上面兩種方法一下子從幾十級別提升到上萬級別。
測試代碼,
@Benchmark@BenchmarkMode({Mode.Throughput})public void m1() { // 獲取當前方法名 String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();}
測試結果,
...# Run progress: 74.99% complete, ETA 00:01:32# Fork: 1 of 2# Warmup Iteration 1: 2191.034 ops/ms# Warmup Iteration 2: 2141.886 ops/ms# Warmup Iteration 3: 2192.843 ops/msIteration 1: 2262.279 ops/msIteration 2: 2263.193 ops/msIteration 3: 2201.354 ops/msIteration 4: 2282.906 ops/msIteration 5: 2130.322 ops/ms# Run progress: 87.48% complete, ETA 00:00:46# Fork: 2 of 2# Warmup Iteration 1: 2207.800 ops/ms# Warmup Iteration 2: 2269.887 ops/ms# Warmup Iteration 3: 2239.005 ops/msIteration 1: 2001.840 ops/msIteration 2: 2047.698 ops/msIteration 3: 2349.138 ops/msIteration 4: 2362.165 ops/msIteration 5: 2305.982 ops/msResult "ltd.newbee.mall.MethodNameTest.m4": 2220.688 ±(99.9%) 186.910 ops/ms [Average] (min, avg, max) = (2001.840, 2220.688, 2362.165), stdev = 123.629 CI (99.9%): [2033.778, 2407.598] (assumes normal distribution)
直接看最后 Result "ltd.newbee.mall.MethodNameTest.m4": 這里,平均 ops 是每毫秒 2220 次。對比 第一種和第二種方案的 幾十 ops 來說性能提升也很客觀,但是對比第三種方法的上萬級別 ops 還是不足。
Benchmark Mode Cnt Score Error UnitsMethodNameTest.m1 thrpt 10 17.267 ± 0.184 ops/msMethodNameTest.m2 thrpt 10 25.280 ± 1.088 ops/msMethodNameTest.m3 thrpt 10 10702.347 ± 672.631 ops/msMethodNameTest.m4 thrpt 10 2220.688 ± 186.910 ops/msMethodNameTest.m1 ss 10 0.686 ± 0.289 ms/opMethodNameTest.m2 ss 10 0.339 ± 0.287 ms/opMethodNameTest.m3 ss 10 0.031 ± 0.011 ms/opMethodNameTest.m4 ss 10 0.074 ± 0.027 ms/op
根據最后得分可以看出,四種方案中性能最好的方案是基于匿名內部類的 getClass().getEnclosingMethod() 方案,性能第二是的是基于 Java 9 出現的 Stack-Walking API 方案,其他兩種性能過于低下了。
本文鏈接:http://www.www897cc.com/showinfo-26-22486-0.html關于四種獲取當前執行方法名稱方案的基準測試報告
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
下一篇: 基于熵的不確定性預測