lambda表達式作為新的技術,一定程度上可以替代匿名類,那麼兩者在執行性能上有什麼差別呢,哪個快,哪個慢,項目實踐時,有哪些注意事項呢。
極客架構師——專注架構師成長。
大家好,我是碼農老吳。
本期是《Java極客》第5期,前面我們分享了lambda表達式的“脫糖”政策,以及它與匿名類的關系,有粉絲問到,lambda表達式性能如何,今天,我就給大家解讀一份orace公司,Java性能工程師Sergey Kuksenko,發表的一份關于lambda表達式與匿名類的性能評比報告。
文章标題
《JDK 8: Lambda Performance study 》(《lambda性能專題論文》)
文章的pdf我已經上傳到我的github,大家感興趣可以下載下傳一下。
作者介紹
Sergey Kuksenko(謝爾蓋·庫克先克),是供職于oracle公司的一名Java Performance Engineer(性能工程師),他的主要工作目标,就是讓Java的JVM運作的更快。研究的範圍主要包括JVM rumtime,JIT compiler,class libraries等等。
重點結論
- 預熱後的加載階段,lambda表達式的執行效率比匿名類稍差,但相差不大。
- 冷啟動的加載階段,lambda表達式的執行效率比匿名類要高的多,相差7倍左右。
- 初始化階段的執行效率,兩者有差距,但是相差不大。
- 執行階段的執行效率,兩者幾乎完全一緻。
執行的三個階段
作者把lambda表達式和匿名類的執行階段,分别概括為如上的三個執行階段,這三個執行階段是一一對應的。為了闡述友善,我把這三個階段,統稱為:
- 加載階段(linkage/class loading),
- 初始化階段(capture/instantiation),
- 執行階段(invocation)。
加載階段
lambda表達式的linkage階段,匿名類的class loading階段,作者這樣命名是非常恰當的,我們在前面分享過,lambda表達式的底層其實是方法,方法調用一般可以稱為linkage(連接配接),而匿名類有自己的獨立位元組碼檔案,是以在使用之前,有class loading(類加載)這個過程。
初始化階段
lambda表達式,是可以通路外部的局部變量或者所在類的執行個體變量的,在lambda表達式裡面,稱之為capture(變量捕獲),而匿名類中,由于它是獨立的類對象,是以它把變量初始化稱之為instantiation(執行個體化)。
執行階段
這是最後一個階段,對于兩者都一樣,就是代碼執行階段,作者稱之為invocation(調用)階段。
測試環境(SUT)
可以看出,作者測試的機器配置并不高,i5CPU, 作業系統是Xubuntu,不過這個文章是2013年發表的,從當時看,配置還算可以。
測試工具-JMH
關于測試工具,作者雖然沒有做過多說明,但是從代碼上可以看出,使用的是JMH,Java Microbenchmark Harness (微基準測試架構),由于後面我們還要分享很多和性能有關的内容,是以我計劃在後面幾期的《極客兵器譜》中,給大家普及一下Java的基準測試相關内容。
加載階段性能
我們先看作者是如何測試加載階段的性能。
要使用lambda表達式,就需要有函數式接口,作者定義了一個函數式接口Level,該接口的up方法,沒有入參,傳回值為Level對象。
從上面的兩頁PPT可以看出,為了測試lambda表達式和匿名類在加載階段的性能,作者可是費了一番功夫,先定義了1024個方法,裡面使用的是lambda表達式,又定義了功能相似的另外1024個方法,裡面使用的是匿名類。總共2048個方法,看來要當性能工程師也不容易,得建立這麼多方法。
下面我們看測試結果。
這是預熱之後的加載階段,lambda的執行效率比匿名類差一些,1K的時候相差将近一半,随着執行次數的增加,兩者的差距越來越小。
這裡面作者還區分了是否啟用TieredCompilation(分層編譯),這個我們在分享JVM的時候再細聊。不過從結果上看,如果啟用了TieredCompilation,兩者的性能都有所提升,比值沒有太大變化。
這是直接冷啟動下加載階段的測試結果,可以看出,匿名類的性能比lambda表達式要差的多,在1K時相差幾乎7倍左右,随着數量增多,相差的比例在縮小,但是直到最後,也相差近兩倍。原因相信大家應該能猜到,原因就出在匿名類它有獨立的位元組碼檔案,這些位元組碼檔案,在首次使用時,必須進行類加載,是以性能會大打折扣。
是以,對于大量使用匿名類的系統,在使用時,系統最好先進行預熱,否則會出現嚴重的性能問題。
而lambda表達式,由于它的底層是方法,是以沒有所謂類加載的過程,冷啟動的性能就好的多。
初始化階段性能
對于這個階段,作者測試的比較細,區分了Non-capture(無捕獲)和capture(有捕獲)兩者情景,測試代碼很簡單,我們直接看測試結果。
從結果看,如果沒有參數捕獲,lambda表達式的性能稍稍比匿名類好一點,但相差不是特别大,唯一要注意的是,匿名類的參數捕獲,在多線程環境下,會急劇的下降。
對于參數捕獲,lambda表達式的性能稍稍比匿名類差一點,但相差也不是特别大。
執行階段性能
對于執行階段的執行性能,作者既沒有給出測試方法,也沒有給出測試結果,隻給了一個結論,就是兩者幾乎完全相同。
也就是對于一段相同的代碼,不論是lambda表達式,或者在匿名類,在執行階段的執行效率,幾乎是一緻的。
下期預告
本期關于lambda表達式和匿名類的性能比較,我們就解讀到這裡,下期,我們繼續分享函數式程式設計的相關内容。下期應該會分享方法引用相關的知識。
極客架構師——專注架構師成長。
我們下期見。