詳解Java編譯優(yōu)化之循環(huán)展開(kāi)和粗化鎖
循環(huán)展開(kāi)和粗化鎖
我們先來(lái)回顧一下什么是循環(huán)展開(kāi)。
循環(huán)展開(kāi)就是說(shuō),像下面的循環(huán)遍歷的例子:
for (int i = 0; i < 1000; i++) { x += 0x51; }
因?yàn)槊看窝h(huán)都需要做跳轉(zhuǎn)操作,所以為了提升效率,上面的代碼其實(shí)可以被優(yōu)化為下面的:
for (int i = 0; i < 250; i++) { x += 0x144; //0x51 * 4 }
注意上面我們使用的是16進(jìn)制數(shù)字,至于為什么要使用16進(jìn)制呢?這是為了方便我們?cè)诤竺娴腶ssembly代碼中快速找到他們。
好了,我們?cè)僭?x += 0x51 的外面加一層synchronized鎖,看一下synchronized鎖會(huì)不會(huì)隨著loop unrolling展開(kāi)的同時(shí)被粗化。
for (int i = 0; i < 1000; i++) { synchronized (this) { x += 0x51; } }
萬(wàn)事具備,只欠我們的運(yùn)行代碼了,這里我們還是使用JMH來(lái)執(zhí)行。
相關(guān)代碼如下:
@Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) @Fork(value = 1, jvmArgsPrepend = { "-XX:-UseBiasedLocking", "-XX:CompileCommand=print,com.flydean.LockOptimization::test" } ) @State(Scope.Benchmark) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public class LockOptimization { int x; @Benchmark @CompilerControl(CompilerControl.Mode.DONT_INLINE) public void test() { for (int i = 0; i < 1000; i++) { synchronized (this) { x += 0x51; } } } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder() .include(LockOptimization.class.getSimpleName()) .build(); new Runner(opt).run(); } }
上面的代碼中,我們?nèi)∠似蜴i的使用:-XX:-UseBiasedLocking。為啥要取消這個(gè)選項(xiàng)呢?因?yàn)槿绻谄蜴i的情況下,如果線程獲得鎖之后,在之后的執(zhí)行過(guò)程中,如果沒(méi)有其他的線程訪問(wèn)該鎖,那么持有偏向鎖的線程則不需要觸發(fā)同步。
為了更好的理解synchronized的流程,這里我們將偏向鎖禁用。
其他的都是我們之前講過(guò)的JMH的常規(guī)操作。
接下來(lái)就是見(jiàn)證奇跡的時(shí)刻了。
分析Assembly日志
我們運(yùn)行上面的程序,將會(huì)得到一系列的輸出。因?yàn)楸疚牟⒉皇侵v解Assembly語(yǔ)言的,所以本文只是大概的理解一下Assembly的使用,并不會(huì)詳細(xì)的進(jìn)行Assembly語(yǔ)言的介紹,如果有想深入了解Assembly的朋友,可以在文后留言。
分析Assembly的輸出結(jié)果,我們可以看到結(jié)果分為C1-compiled nmethod和C2-compiled nmethod兩部分。
先看C1-compiled nmethod:
第一行是monitorenter,表示進(jìn)入鎖的范圍,后面還跟著對(duì)于的代碼行數(shù)。
最后一行是monitorexit,表示退出鎖的范圍。
中間有個(gè)add $0x51,%eax操作,對(duì)于著我們的代碼中的add操作。
可以看到C1—compiled nmethod中是沒(méi)有進(jìn)行Loop unrolling的。
我們?cè)倏纯碈2-compiled nmethod:
和C1很類似,不同的是add的值變成了0x144,說(shuō)明進(jìn)行了Loop unrolling,同時(shí)對(duì)應(yīng)的鎖范圍也跟著進(jìn)行了擴(kuò)展。
最后看下運(yùn)行結(jié)果:
Benchmark Mode Cnt Score Error Units
LockOptimization.test avgt 5 5601.819 ± 620.017 ns/op
得分還不錯(cuò)。
禁止Loop unrolling
接下來(lái)我們看下如果將Loop unrolling禁掉,會(huì)得到什么樣的結(jié)果。
要禁止Loop unrolling,只需要設(shè)置-XX:LoopUnrollLimit=1即可。
我們?cè)龠\(yùn)行一下上面的程序:
可以看到C2-compiled nmethod中的數(shù)字變成了原本的0x51,說(shuō)明并沒(méi)有進(jìn)行Loop unrolling。
再看看運(yùn)行結(jié)果:
Benchmark Mode Cnt Score Error Units
LockOptimization.test avgt 5 20846.709 ± 3292.522 ns/op
可以看到運(yùn)行時(shí)間基本是優(yōu)化過(guò)后的4倍左右。說(shuō)明Loop unrolling還是非常有用的。
以上就是詳解Java編譯優(yōu)化之循環(huán)展開(kāi)和粗化鎖的詳細(xì)內(nèi)容,更多關(guān)于Java編譯優(yōu)化之循環(huán)展開(kāi)和粗化鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Mybatis動(dòng)態(tài)sql超詳細(xì)講解
動(dòng)態(tài)SQL是MyBatis的強(qiáng)大特性之一,顧名思義就是會(huì)動(dòng)的SQL,即是能夠靈活的根據(jù)某種條件拼接出完整的SQL語(yǔ)句,下面這篇文章主要給大家介紹了關(guān)于Mybatis動(dòng)態(tài)sql的相關(guān)資料,需要的朋友可以參考下2023-04-04Java8 HashMap擴(kuò)容算法實(shí)例解析
這篇文章主要介紹了Java8 HashMap擴(kuò)容算法實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12Java實(shí)現(xiàn)四則混合運(yùn)算代碼示例
這篇文章主要介紹了Java實(shí)現(xiàn)四則混合運(yùn)算代碼示例,文中展示了詳細(xì)代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10實(shí)例講解Java的Spring框架中的AOP實(shí)現(xiàn)
這篇文章主要介紹了Java的Spring框架中的AOP實(shí)現(xiàn)實(shí)例,AOP面向切面編程其實(shí)也可以被看作是一個(gè)設(shè)計(jì)模式去規(guī)范項(xiàng)目的結(jié)構(gòu),需要的朋友可以參考下2016-04-04SpringBoot+Mybatis使用Enum枚舉類型總是報(bào)錯(cuò)No enum constant&n
這篇文章主要介紹了SpringBoot+Mybatis使用Enum枚舉類型總是報(bào)錯(cuò)No enum constant XX問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12