如果你想寫自己的Benchmark框架(推薦)
簡介
使用過JMH的同學(xué)一定會(huì)驚嘆它的神奇。JMH作為一個(gè)優(yōu)秀的Benchmark框架帶給了我們無數(shù)的歡樂。作為一個(gè)有極客精神的程序員,那么有沒有想過去自己實(shí)現(xiàn)一個(gè)Benchmark框架呢?
在實(shí)現(xiàn)Benchmark框架的時(shí)候有需要注意些什么問題呢?快來一起看看吧。
八條軍規(guī)
這里叫軍規(guī)實(shí)際上不合適,只是借用一下軍規(guī)的來彰顯一下氣勢!大家不要太介意。
第一條軍規(guī)
工欲善其事,必先利其器。想寫好一個(gè)JMH當(dāng)然需要深入了解JVM的運(yùn)行原理,包括JIT,C1,C2編譯器和他們的分層編譯原理,JIT運(yùn)行時(shí)的編譯優(yōu)化,包括Loop unrolling, Inlining, Dead Code Elimination,
Escape analysis, Intrinsics, Branch prediction等等。
當(dāng)然,最好是參考一下大牛們寫過的JMH框架,找點(diǎn)靈感。
最后大家要了解,Benchmark框架不是萬能的。它只是在特定的環(huán)境中JVM的表現(xiàn)。
因?yàn)樵贐enchmark中我們肯定是要做循環(huán)的,一般來說就是某某方法運(yùn)行多少次,這種比較簡單的循環(huán)。實(shí)際上,JVM運(yùn)行的代碼是非常復(fù)雜的。Benchmark遠(yuǎn)遠(yuǎn)不能代表JVM的全部。
但是,見微知著,使用Benchmark還是可以一窺JVM的秘密的。
第二條軍規(guī)
在JMH中,我們一般需要設(shè)置warmup和measurement的次數(shù):
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
這是為什么呢?我們知道JIT中的代碼是動(dòng)態(tài)編譯成為機(jī)器碼的,并且是需要一定的時(shí)間的。
只有JIT檢測到你這是熱點(diǎn)代碼,才會(huì)對(duì)其進(jìn)行優(yōu)化。
我們檢測代碼的性能,一般是指代碼在穩(wěn)定運(yùn)行的環(huán)境中的情形。而不是指第一次或者前幾次運(yùn)行的時(shí)候,因?yàn)檫@個(gè)時(shí)候,這些代碼可能并沒有被編譯成機(jī)器碼。這樣的出來的結(jié)果往往是和實(shí)際不相符的。
第三條軍規(guī)
在編寫B(tài)enchmark的同時(shí),一定要開啟JVM的日志。例如: -XX:+PrintCompilation, -verbose:gc等。
為什么呢?
大家想想benchmark是做什么的呢?就是統(tǒng)計(jì)時(shí)間的。
我們希望在運(yùn)行benchmark的時(shí)候,JVM不要做任何不屬于運(yùn)行代碼的任何事情,否則就可能會(huì)影響到benchmark的準(zhǔn)確性。
所以開啟JVM的日志就是為了做校驗(yàn)。不要在做benchmark的時(shí)候有其他操作。
第四條軍規(guī)
注意JIT的分層編譯。
因?yàn)镃lient VM和Server VM的出現(xiàn),所以在JIT中出現(xiàn)了兩種不同的編譯器,C1 for Client VM, C2 for Server VM。
因?yàn)閖avac的編譯只能做少量的優(yōu)化,其實(shí)大量的動(dòng)態(tài)優(yōu)化是在JIT中做的。C2相對(duì)于C1,其優(yōu)化的程度更深,更加激進(jìn)。
為了更好的提升編譯效率,JVM在JDK7中引入了分層編譯Tiered compilation的概念。
對(duì)于JIT本身來說,動(dòng)態(tài)編譯是需要占用用戶內(nèi)存空間的,有可能會(huì)造成較高的延遲。
對(duì)于Server服務(wù)器來說,因?yàn)榇a要服務(wù)很多個(gè)client,所以磨刀不誤砍柴工,短暫的延遲帶來永久的收益,聽起來是可以接受的。
Server端的JIT編譯也不是立馬進(jìn)行的,它可能需要收集到足夠多的信息之后,才進(jìn)行編譯。
而對(duì)于Client來說,延遲帶來的性能影響就需要進(jìn)行考慮了。和Server相比,它只進(jìn)行了簡單的機(jī)器碼的編譯。
為了滿足不同層次的編譯需求,于是引入了分層編譯的概念。
大概來說分層編譯可以分為三層:
- 第一層就是禁用C1和C2編譯器,這個(gè)時(shí)候沒有JIT進(jìn)行。
- 第二層就是只開啟C1編譯器,因?yàn)镃1編譯器只會(huì)進(jìn)行一些簡單的JIT優(yōu)化,所以這個(gè)可以應(yīng)對(duì)常規(guī)情況。
- 第三層就是同時(shí)開啟C1和C2編譯器。
在JDK7中,你可以使用下面的命令來開啟分層編譯:
-XX:+TieredCompilation
而在JDK8之后,恭喜你,分層編譯已經(jīng)是默認(rèn)的選項(xiàng)了,不用再手動(dòng)開啟。
Client編譯和Server編譯,甚至是OSR都是不同的。大家在寫B(tài)enchmark的時(shí)候一定要注意。
第五條軍規(guī)
注意初始化對(duì)性能的影響。
如果需要加載類,一定要在warmup的階段進(jìn)行加載,除非你是想去測試加載的時(shí)間。否則會(huì)對(duì)測試結(jié)果有影響。
同時(shí)也不要計(jì)算第一次print的時(shí)間,因?yàn)閜rint也會(huì)加載和初始化一些類。
第六條軍規(guī)
要注意反優(yōu)化和重編譯的影響。
JIT在下面的幾個(gè)特殊的情況下,需要對(duì)代碼進(jìn)行返優(yōu)化:
有些特殊的情況下面,確實(shí)是需要進(jìn)行反優(yōu)化的。
下面是比較常見的情況:
1.需要調(diào)試的情況
如果代碼正在進(jìn)行單個(gè)步驟的調(diào)試,那么之前被編譯成為機(jī)器碼的代碼需要反優(yōu)化回來,從而能夠調(diào)試。
2.代碼廢棄的情況
當(dāng)一個(gè)被編譯過的方法,因?yàn)榉N種原因不可用了,這個(gè)時(shí)候就需要將其反優(yōu)化。
3.優(yōu)化之前編譯的代碼
有可能出現(xiàn)之前優(yōu)化過的代碼可能不夠完美,需要重新優(yōu)化的情況,這種情況下同樣也需要進(jìn)行反優(yōu)化。
重編譯是指JIT可能會(huì)重新優(yōu)化代碼,導(dǎo)致重新編譯。
所以這條規(guī)則要求我們warmup的時(shí)間要盡可能的長。以便讓JIT充分優(yōu)化。
第七條軍規(guī)
在使用benchMark得出結(jié)論之前,一定要去認(rèn)真的理解JVM的底層代碼(Assembly code),找到其現(xiàn)象的本質(zhì)。
千萬不要沖動(dòng)的下結(jié)論。最好是使用可視化的工具來分析。比如說jitwatch。
最后一條軍規(guī)
在測試的時(shí)候一定要避免其他程序的影響 。
比如說兩次測試,第一次測試是單機(jī)運(yùn)行,第二次測試是在有其他服務(wù)正在運(yùn)行的情況下進(jìn)行的。
很顯然這兩次的結(jié)果是不能做比較的。我們需要多運(yùn)行,剔除噪音結(jié)果。
總結(jié)
掌握上面幾條規(guī)則,相信大家也能夠?qū)懗鰧儆谧约旱腂enchmarks。
到此這篇關(guān)于如果你想寫自己的Benchmark框架的文章就介紹到這了,更多相關(guān)Benchmark框架內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java深入學(xué)習(xí)圖形用戶界面GUI之布局管理器
本文章向大家介紹Java GUI布局管理器,主要包括布局管理器使用實(shí)例、應(yīng)用技巧、基本知識(shí)點(diǎn)總結(jié)和需要注意事項(xiàng),具有一定的參考價(jià)值,需要的朋友可以參考一下2022-05-05Springboot啟動(dòng)后執(zhí)行方法小結(jié)
本文主要介紹了Springboot啟動(dòng)后執(zhí)行方法小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04java方法重寫和super關(guān)鍵字實(shí)例詳解
這篇文章主要介紹了java方法重寫和super關(guān)鍵字實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03SpringBoot SpEL語法掃盲與查詢手冊的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot SpEL語法掃盲與查詢手冊的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05Java中的Sentinel持久化規(guī)則啟動(dòng)
這篇文章主要介紹了Java中的Sentinel持久化規(guī)則啟動(dòng),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-08-08