Java求余%操作引發(fā)的一連串故事
操作符%通常用在正整數上,但同樣可以用在負整數和浮點數上。
注意:只有當被除數是負數時, 余數才是負的。
C1 RCE對%的處理
HotSpot VM的C1有個RCE(Range Check Elimination,范圍檢查消除)優(yōu)化,所謂范圍檢查消除,就是為了正確的拋出數組越界異常,虛擬機需要在數組訪問的一些地方插入隱式的檢查,但是這些檢查會降低性能,比如在循環(huán)中每次循環(huán)都得檢查一次,所以HotSpot VM會想辦法在可能的地方消除這些檢查。我在看C1 RCE的時候發(fā)現目前它對求余符號的支持較為薄弱,它只能處理形如下面的代碼:
arr[x%arr.length] // 只有除數是x.length的時候,才能應用RCE優(yōu)化
如果余數是整數常量,它就不能工作了:
arr[x%3] for(int i=0;i<10;i++){ arr[x%10] }
實際上,根據JLS的定義,我們知道如果除數為整數常量(且等于零,因為0作為除數會拋出運行時異常),是可以推導出結果的上下界的(也取決于被除數的正負),規(guī)則如下:
- x % -y ==> [0, y - 1]
- x % y ==> [0, y - 1]
- -x % y ==> [-y + 1, 0]
- -x % -y ==> [-y + 1, 0]
于是,我給JDK發(fā)了個patch,這個問題算是解決了。但是Nils提到,C2是否有相同的優(yōu)化呢?后面Tobias幫忙確認了一下C2沒有,我再后來也進一步確認了,所以下一步是調研C2是否能應用同樣的優(yōu)化。
調研為C2應用同樣的優(yōu)化
本來以為是比較trivial的事情,為求余節(jié)點的類型系統加點代碼,推導一下上下界即可,實際上我也這么做的,但是最后發(fā)現這樣沒有消除上下界。默認開啟-XX:+GenerateRangeChecks后,在數組訪問過程中(Parse::array_addressing
),C2仍然生成了范圍檢查。
調試后發(fā)現推導上下界根本沒有執(zhí)行,因為C2創(chuàng)建完求余節(jié)點后,會執(zhí)行一個IGVN的過程,即迭代的應用多種優(yōu)化,其中就包括理想化,C2理想化是指應用很多局部小優(yōu)化的過程,在這個例子中就是特殊處理形如x%2^n
,x%2^n-1
和x%1
的情況,如果除數是整數常量,它還會使用一個來自https://book.douban.com/subject/1784887/書里面的算法,即Division by Invariant Integers using Multiplication(by Granlund and Montgomery),搜了一下知乎有類似的文章,想要了解細節(jié)可以讀讀https://zhuanlan.zhihu.com/p/151038723。知道了原因,于是我改了下代碼,禁止了求余節(jié)點的理想化,心想這總可以了吧。
還是不行
是的,還是不行。盡管我已經禁止了對求余符號的理想化優(yōu)化,但是范圍檢查還是生成了。。。我又繼續(xù)看代碼,發(fā)現除了理想化的這個優(yōu)化之外,C2在IR(中間表示)構造的過程中又 又 又 又 又對求余運算做了個優(yōu)化!如果除數是正整數常量,且是2^n
,那么C2會對它進行變形,IR如圖所示:
左邊的IR是 IR構造的時候C2做的優(yōu)化后的效果,右邊是理想化優(yōu)化后的效果。實際上它們做的事情本身是比較重復的,而且經過測試發(fā)現,理想化優(yōu)化的算法要好于IR構造過程中的優(yōu)化,所以我又提了個patch解決這個問題(不過還在review中)。
結語
我認為為求余節(jié)點推導上下界也是有意義的,如果以后有其他優(yōu)化會變形為求余運算,那么它們可以應用這個推導,同時,為求余做統一完善的類型推導這件事本身也是正確的,所以我又提了個patch??梢钥吹?,最終我只消除了C1 arr[x%4]的范圍檢查,還是沒能消除C2 arr[x%4]的范圍檢查,是不是以后可以說C1有的地方做的比C2好了(狗頭hh。
以上就是Java求余%操作引發(fā)的一連串故事的詳細內容,更多關于Java求余%的資料請關注腳本之家其它相關文章!
相關文章
Springboot基于assembly的服務化打包方案及spring boot部署方式
這篇文章主要介紹了Springboot基于assembly的服務化打包方案及springboot項目的幾種常見的部署方式,本文主要針對第二種部署方式提供一種更加友好的打包方案,需要的朋友可以參考下2017-12-12Java?Springboot異步執(zhí)行事件監(jiān)聽和處理實例
Java?SpringBoot中,監(jiān)聽和處理事件是一種常見的模式,它允許不同的組件之間通過事件進行通信,事件監(jiān)聽和處理通常通過Spring的事件發(fā)布-訂閱模型來實現,一個簡單的Spring?Boot應用程序示例,其中將包括事件的定義、事件的發(fā)布以及事件的監(jiān)聽2024-07-07SpringBoot實現本地存儲文件上傳及提供HTTP訪問服務的方法
這篇文章主要介紹了SpringBoot實現本地存儲文件上傳及提供HTTP訪問服務,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-08-08