使用過JMH的同窗必定會驚歎它的神奇。JMH做爲一個優秀的Benchmark框架帶給了咱們無數的歡樂。做爲一個有極客精神的程序員,那麼有沒有想過去本身實現一個Benchmark框架呢?java
在實現Benchmark框架的時候有須要注意些什麼問題呢?快來一塊兒看看吧。程序員
這裏叫軍規實際上不合適,只是借用一下軍規的來彰顯一下氣勢!你們不要太介意。服務器
工欲善其事,必先利其器。想寫好一個JMH固然須要深刻了解JVM的運行原理,包括JIT,C1,C2編譯器和他們的分層編譯原理,JIT運行時的編譯優化,包括Loop unrolling, Inlining, Dead Code Elimination,
Escape analysis, Intrinsics, Branch prediction等等。框架
固然,最好是參考一下大牛們寫過的JMH框架,找點靈感。工具
最後你們要了解,Benchmark框架不是萬能的。它只是在特定的環境中JVM的表現。oop
由於在Benchmark中咱們確定是要作循環的,通常來講就是某某方法運行多少次,這種比較簡單的循環。實際上,JVM運行的代碼是很是複雜的。Benchmark遠遠不能表明JVM的所有。性能
可是,見微知著,使用Benchmark仍是能夠一窺JVM的祕密的。測試
在JMH中,咱們通常須要設置warmup和measurement的次數:優化
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
這是爲何呢?咱們知道JIT中的代碼是動態編譯成爲機器碼的,而且是須要必定的時間的。spa
只有JIT檢測到你這是熱點代碼,纔會對其進行優化。
咱們檢測代碼的性能,通常是指代碼在穩定運行的環境中的情形。而不是指第一次或者前幾回運行的時候,由於這個時候,這些代碼可能並無被編譯成機器碼。這樣的出來的結果每每是和實際不相符的。
在編寫Benchmark的同時,必定要開啓JVM的日誌。例如: -XX:+PrintCompilation, -verbose:gc等。
爲何呢?
你們想一想benchmark是作什麼的呢?就是統計時間的。
咱們但願在運行benchmark的時候,JVM不要作任何不屬於運行代碼的任何事情,不然就可能會影響到benchmark的準確性。
因此開啓JVM的日誌就是爲了作校驗。不要在作benchmark的時候有其餘操做。
注意JIT的分層編譯。
由於Client VM和Server VM的出現,因此在JIT中出現了兩種不一樣的編譯器,C1 for Client VM, C2 for Server VM。
由於javac的編譯只能作少許的優化,其實大量的動態優化是在JIT中作的。C2相對於C1,其優化的程度更深,更加激進。
爲了更好的提高編譯效率,JVM在JDK7中引入了分層編譯Tiered compilation的概念。
對於JIT自己來講,動態編譯是須要佔用用戶內存空間的,有可能會形成較高的延遲。
對於Server服務器來講,由於代碼要服務不少個client,因此磨刀不誤砍柴工,短暫的延遲帶來永久的收益,聽起來是能夠接受的。
Server端的JIT編譯也不是立馬進行的,它可能須要收集到足夠多的信息以後,才進行編譯。
而對於Client來講,延遲帶來的性能影響就須要進行考慮了。和Server相比,它只進行了簡單的機器碼的編譯。
爲了知足不一樣層次的編譯需求,因而引入了分層編譯的概念。
大概來講分層編譯能夠分爲三層:
在JDK7中,你可使用下面的命令來開啓分層編譯:
-XX:+TieredCompilation
而在JDK8以後,恭喜你,分層編譯已是默認的選項了,不用再手動開啓。
Client編譯和Server編譯,甚至是OSR都是不一樣的。你們在寫Benchmark的時候必定要注意。
注意初始化對性能的影響。
若是須要加載類,必定要在warmup的階段進行加載,除非你是想去測試加載的時間。不然會對測試結果有影響。
同時也不要計算第一次print的時間,由於print也會加載和初始化一些類。
要注意反優化和重編譯的影響。
JIT在下面的幾個特殊的狀況下,須要對代碼進行返優化:
有些特殊的狀況下面,確實是須要進行反優化的。
下面是比較常見的狀況:
若是代碼正在進行單個步驟的調試,那麼以前被編譯成爲機器碼的代碼須要反優化回來,從而可以調試。
當一個被編譯過的方法,由於種種緣由不可用了,這個時候就須要將其反優化。
有可能出現以前優化過的代碼可能不夠完美,須要從新優化的狀況,這種狀況下一樣也須要進行反優化。
重編譯是指JIT可能會從新優化代碼,致使從新編譯。
因此這條規則要求咱們warmup的時間要儘量的長。以便讓JIT充分優化。
在使用benchMark得出結論以前,必定要去認真的理解JVM的底層代碼(Assembly code),找到其現象的本質。
千萬不要衝動的下結論。最好是使用可視化的工具來分析。好比說jitwatch。
在測試的時候必定要避免其餘程序的影響 。
好比說兩次測試,第一次測試是單機運行,第二次測試是在有其餘服務正在運行的狀況下進行的。
很顯然這兩次的結果是不能作比較的。咱們須要多運行,剔除噪音結果。
掌握上面幾條規則,相信你們也可以寫出屬於本身的Benchmarks。
本文連接: http://www.flydean.com/how-to-write-benchmarks/最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!
歡迎關注個人公衆號:「程序那些事」,懂技術,更懂你!