[譯]使用JMH進行微基準測試:不要猜,要測試!

来源:http://www.cnblogs.com/Binhua-Liu/archive/2016/06/26/5617995.html
-Advertisement-
Play Games

英文原文:Micro Benchmarking with JMH: Measure, don’t guess!翻譯地址:使用JMH進行微基準測試:不要猜,要測試!原文作者:Antonio翻譯作者:Hollis轉載請註明出處。 很多Java開發人員都知道把一個變數聲明為null有助於垃圾回收(譯者註:... ...


英文原文:Micro Benchmarking with JMH: Measure, don’t guess!
翻譯地址:使用JMH進行微基準測試:不要猜,要測試!
原文作者:Antonio
翻譯作者:Hollis
轉載請註明出處。

 

很多Java開發人員都知道把一個變數聲明為null有助於垃圾回收(譯者註:一般而言,為null的對象都會被作為垃圾處理,所以將不用的對象顯式地設為Null,有利於GC收集器判定垃圾,從而提高了GC的效率。),也有部分人知道使用final定義一個內聯方法能夠提高性能。但是,我們也知道,如今,JVM在不斷進化,很多你昨天認定的理論到了今天則不一定試用了。(譯者註:因為jvm的不斷優化,大多數時候,即時我們不把不用的變數聲明為null,垃圾回收器也能很快判斷出該對象是否應該被回收。jvm的不斷優化之後,把變數設置為null這一舉動可能並不會帶來顯著的性能提升)那麼,我們如何能夠知道我們寫的代碼是否高效呢?其實,我們不應該去猜測,而是動手去測試。

 

Measure, don’t guess!

就像我的朋友 Kirk Pepperdine once said說的那樣 “Measure, don’t guess“. 當我們的代碼出現性能問題的時候,我們總是試圖做一些小的改動(很可能是隨意的改動)希望能對性能有所提升。相反,我們應該建立一個穩定的性能測試環境(包括操作系統,jvm,應用伺服器,資料庫等),設置一些性能目標,針對這一目標不斷的進行測試,直到達到你的預期。和持續測試、持續交付類似,我們也應該進行持續的性能測試。

 

無論如何,性能都是一個黑暗藝術,這不是這篇文章討論的主要內容。我只是想關註微基準測試和向您展示如何在一個真是的用例(本文以日誌記錄為例)中使用JMH

 

在日誌輸出中使用微基準測試

相信很多人和我一樣,在使用了多個日誌框架之後,肯定見過下麵這些調試日誌:

logger.debug("Concatenating strings " + x + y + z);

logger.debug("Using variable arguments {} {} {}", x, y, z);

if (logger.isDebugEnabled())
  logger.debug("Using the if debug enabled {} {} {}", x, y, z);

 

在一般的應用中,日誌輸出級別都是INFO或者WARNING。即使使用了WARNING級別,上面這幾斷代碼都可以正常輸出調試信息。但是,調試日誌可以卻可以影響應用的表現(性能)。為了證明這一點,我們將使用微基準測試來測試以上三種代碼的性能,這裡使用Java微基準測試工具(JMH)。上面的三種代碼分別可以概括為:使用字元串連接、使用變數參數和使用If進行debug可用檢測。

 

JMH設置

JMH是一個用於java或者其他JVM語言的,提供構建,運行和分析(按照多種基準:納秒,微妙、毫秒、巨集)的工具。通過maven archtype我們可以很快的創建一個JMH工程。

mvn archetype:generate -DinteractiveMode=false -DarchetypeGroupId=org.openjdk.jmh \
      -DarchetypeArtifactId=jmh-java-benchmark-archetype -DarchetypeVersion=1.4.1 \
      -DgroupId=org.agoncal.sample.jmh -DartifactId=logging -Dversion=1.0

 

使用該maven原型創建出來的項目結構如下:

一個包含了JMH相關依賴和maven-shade-plugin插件的pom文件

一個使用了@Benchmark註解的空的MyBenchmark文件

 

jmhproject

 

這個時候,雖然我們是什麼都還沒做,但是我們剛剛創建的微基準測試項目已經可以啟動並運行了。使用maven命令打包就能生成一個benchmarks.jar

mvn clean install
 java -jar target/benchmarks.jar

 

當我們使用以上命令運行這個jar時,我們就可以在控制臺上看到一些有趣的內容輸出:JMH進入迴圈、預熱JVM,執行@Benchmark註解的空方法,並給出每秒操作的數量。

# Run progress: 30,00% complete, ETA 00:04:41
# Fork: 4 of 10
# Warmup Iteration   1: 2207650172,188 ops/s
# Warmup Iteration   2: 2171077515,143 ops/s
# Warmup Iteration   3: 2147266359,269 ops/s
# Warmup Iteration   4: 2193541731,837 ops/s
# Warmup Iteration   5: 2195724915,070 ops/s
# Warmup Iteration   6: 2191867717,675 ops/s
# Warmup Iteration   7: 2143952349,129 ops/s
# Warmup Iteration   8: 2187759638,895 ops/s
# Warmup Iteration   9: 2171283214,772 ops/s
# Warmup Iteration  10: 2194607294,634 ops/s
# Warmup Iteration  11: 2195047447,488 ops/s
# Warmup Iteration  12: 2191714465,557 ops/s
# Warmup Iteration  13: 2229074852,390 ops/s
# Warmup Iteration  14: 2221881356,361 ops/s
# Warmup Iteration  15: 2240789717,480 ops/s
# Warmup Iteration  16: 2236822727,970 ops/s
# Warmup Iteration  17: 2228958137,977 ops/s
# Warmup Iteration  18: 2242267603,165 ops/s
# Warmup Iteration  19: 2216594798,060 ops/s
# Warmup Iteration  20: 2243117972,224 ops/s
Iteration   1: 2201097704,736 ops/s
Iteration   2: 2224068972,437 ops/s
Iteration   3: 2243832903,895 ops/s
Iteration   4: 2246595941,792 ops/s
Iteration   5: 2241703372,299 ops/s
Iteration   6: 2243852186,017 ops/s
Iteration   7: 2221541382,551 ops/s
Iteration   8: 2196835756,509 ops/s
Iteration   9: 2205740069,844 ops/s
Iteration  10: 2207837588,402 ops/s
Iteration  11: 2192906907,559 ops/s
Iteration  12: 2239234959,368 ops/s
Iteration  13: 2198998566,646 ops/s
Iteration  14: 2201966804,597 ops/s
Iteration  15: 2215531292,317 ops/s
Iteration  16: 2155095714,297 ops/s
Iteration  17: 2146037784,423 ops/s
Iteration  18: 2139622262,798 ops/s
Iteration  19: 2213499245,208 ops/s
Iteration  20: 2191108429,343 ops/s

 

向基準中添加SFL4J

前面不是說過嗎,我們要測試的用例是日誌記錄,那麼在這個項目中我將使用SFL4J和Logback,我們向pom文件中增加依賴:

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.7</version>
</dependency>
<dependency>
  <groupId>ch.qos.logback</groupId>
  <artifactId>logback-classic</artifactId>
  <version>1.0.11</version>
</dependency>

 

然後我們增加一個logback.xml配置文件,並設置日誌輸出級別為INFO

<configuration>
  <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%highlight(%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n)</pattern>
    </encoder>
  </appender>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder><pattern>%msg%n</pattern></encoder>
  </appender>

  <root level="INFO">
    <appender-ref ref="CONSOLE" />
  </root>
</configuration>

 

使用maven-shade-plugin的好處是,當我們使用maven對應用進行打包的時候,所有的依賴和配置文件等都會打包到target目錄下。

 

在日誌中使用字元串連接

開始第一個微基準測試:在日誌中使用字元串連接。這裡我們將所需代碼寫到由@Benchmark註解標註的方法中,然後其他的事情就交給JMH。

 

這段代碼中,我們創建x,y,z三個字元串變數,然後在迴圈中,使用字元串連接的形式將調試日誌輸出。代碼如下:

import org.openjdk.jmh.annotations.Benchmark;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyBenchmark {

  private static final Logger logger = LoggerFactory.getLogger(MyBenchmark.class);

  @Benchmark
  public void testConcatenatingStrings() {

    String x = "", y = "", z = "";

    for (int i = 0; i < 100; i++) {
      x += i; y += i; z += i;

      logger.debug("Concatenating strings " + x + y + z);
    }
  }
}

 

然後還是像剛剛一樣,運行這個微基準測試,並查看迭代輸出。

譯者註:後文將統一進行對比。

 

在日誌中使用變數參數

這個微基準測試中,我們使用變數參數來代替字元串連接,更改代碼內容如下,然後打包執行。

@Benchmark
public void testVariableArguments() {

  String x = "", y = "", z = "";

  for (int i = 0; i < 100; i++) {
    x += i; y += i; z += i;

    logger.debug("Variable arguments {} {} {}", x, y, z);
  }
}

 

在日誌中使用If判斷語句

最後一個也是最重要的一個,使用日誌輸出時使用isDebugEnabled()進行優化

@Benchmark
public void testIfDebugEnabled() {

  String x = "", y = "", z = "";

  for (int i = 0; i < 100; i++) {
    x += i; y += i; z += i;

    if (logger.isDebugEnabled())
      logger.debug("If debug enabled {} {} {}", x, y, z);
  }
}

 

微基準測試的結果

在運行三個微基準測試之後,我們將預期結果(記住,don’t guess, measure)。每秒的操作次數越多,表示性能越好。如果我們看看下表的最後一行,我們註意到使用isDebugEnabled的性能最好,使用字元串連接最糟糕。同時也能發現,在沒有使用isDebugEnabled而是使用變數參數的測試結果並不差。 綜合代碼的可讀性(較少的boilerplate code(模塊化代碼,也可以理解為不重要,但是又不可缺少的代碼)) 。所以我會選擇使用變數參數的那種形式!

String concatenation Variable arguments if isDebugEnabled
Iteration 1 57108,635 ops/s 97921,939 ops/s 104993,368 ops/s
Iteration 2 58441,293 ops/s 98036,051 ops/s 104839,216 ops/s
Iteration 3 58231,243 ops/s 97457,222 ops/s 106601,803 ops/s
Iteration 4 58538,842 ops/s 100861,562 ops/s 104643,717 ops/s
Iteration 5 57297,787 ops/s 100405,656 ops/s 104706,503 ops/s
Iteration 6 57838,298 ops/s 98912,545 ops/s 105439,939 ops/s
Iteration 7 56645,371 ops/s 100543,188 ops/s 102893,089 ops/s
Iteration 8 56569,236 ops/s 102239,005 ops/s 104730,682 ops/s
Iteration 9 57349,754 ops/s 94482,508 ops/s 103492,227 ops/s
Iteration 10 56894,075 ops/s 101405,938 ops/s 106790,525 ops/s
Average 57491,4534 ops/s 99226,5614 ops/s 104913,1069 ops/s

 

## 結論
在過去的幾十年jvm大幅進化。用十年前的設計模式優化我們的代碼是不可取的。比較兩段代碼的好壞的唯一辦法就是測量它。JMH就是一個簡單高效的進行微基準測試的完美工具。當然,推理的一小部分代碼只有一個步驟,因為我們通常需要分析整個應用程式的性能。因為有了HMH,讓這個第一個步驟變得很容易。

這還有一些JMH的例子,它充滿了有趣的想法。

 

參考資料

JMH Samples

Java Performance Tuning Guide

Using JMH for Java Microbenchmarking

Writing Java Micro Benchmarks with JMH: Juicy

【公告】版權聲明

(全文完)


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Matplotlib作為Python中著名的數據可視化工具,其官網也提供了在PyQt4中使用的源碼,這裡舉一個應用實例,以備不時之需。 1) 利用Qt Designer創建GUI界面 Demo的GUI界面,如圖1所示,其中利用QFrame作為放置Matplotlib界面的容器。然後調用pyuic4. ...
  • 1.header PHP文件插入header("Content-type: text/html; charset=utf-8");相當於頁面裡面的<meta http-equiv="Content-Type" content="text/html; charset=utf-8">;目的:防止頁面出現 ...
  • 1. 一般來說,導入objective c的頭文件時用#import,包含c/c++頭文件時用#include。 2. #import 確定一個文件只能被導入一次,這使你在遞歸包含中不會出現問題。<標記> 所以,#import比起#include的好處就是不會引起交叉編譯。 #import && # ...
  • 一、利用google提供的的zxing生成不包含logo的二維碼 運行main方法後在: 二、利用google的zxing生成包含logo的二維碼 使用簡介: 在QEcodeUtil.java類中: 運行main方法後生成的二維碼: 三、使用Jquery.qrcode生成二維碼 下載項目使用: 代碼 ...
  • php-resque是一個輕量級的消息隊列,讓我們一起來瞭解一下它的設計和使用 ...
  • HELLO WORLD! 今天第一次開立博客,專門記載學習編程語言中的點點滴滴。今日學習內容: 搭建vs code+ python3.5 "ide" 學習廖雪峰python教程-使用list和tuple 因為之前有pascal基礎,所以學起來不是很難理解,但是貴在堅持,自我激勵。昨天《墊底辣妹》給打 ...
  • 題目:輸入一個矩陣,按照從外向里以順時針的順序依次列印出每一個數字。 例如輸入以下矩陣: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 則依次列印出數字 1、2、3、4、8、12、16、15、14、13、9、5、6、7、11、10 ...
  • 列印基本類型 以下列印基本的數據類型, 如int, char, float等, 最後兩行是以八進位和十六進位列印數字10 windows gcc輸出: 設置輸出寬度 設置每個整數占10個位置, 預設為右對齊 如果數字的長度比設置的寬度大, 那麼會忽略我們設置的輸出寬度 windows gcc輸出: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...