西安達(dá)內(nèi)培訓(xùn)(www.xatarena.cn)講師表示,Java語言中JVM重排序通常是編譯器或運(yùn)行時(shí)環(huán)境為了優(yōu)化程序性能而采取的對指令進(jìn)行重新排序執(zhí)行的一種手段。重排序分為兩類:編譯期重排序和運(yùn)行期重排序,分別對應(yīng)編譯時(shí)和運(yùn)行時(shí)環(huán)境在并發(fā)程序中,程序員會特別關(guān)注不同進(jìn)程或線程之間的數(shù)據(jù)同步,特別是多個(gè)線程同時(shí)修改同一變量時(shí),必須采取可靠的同步或其它措施保障數(shù)據(jù)被正確地修改,這里的一條重要原則是:不要假設(shè)指令執(zhí)行的順序,你無法預(yù)知不同線程之間的指令會以何種順序執(zhí)行。
JVM編譯期重排序
編譯期重排序的典型就是通過調(diào)整指令順序,在不改變程序語義的前提下,盡可能減少寄存器的讀取、存儲次數(shù),充分復(fù)用寄存器的存儲值。
假設(shè)第一條指令計(jì)算一個(gè)值賦給變量A并存放在寄存器中,第二條指令與A無關(guān)但需要占用寄存器(假設(shè)它將占用A所在的那個(gè)寄存器),第三條指令使用A的值且與第二條指令無關(guān)。那么如果按照順序一致性模型,A在第一條指令執(zhí)行過后被放入寄存器,在第二條指令執(zhí)行時(shí)A不再存在,第三條指令執(zhí)行時(shí)A重新被讀入寄存器,而這個(gè)過程中,A的值沒有發(fā)生變化。通常編譯器都會交換第二和第三條指令的位置,這樣第一條指令結(jié)束時(shí)A存在于寄存器中,接下來可以直接從寄存器中讀取A的值,降低了重復(fù)讀取的開銷。
JVM重排序?qū)τ诹魉€的意義
現(xiàn)代CPU幾乎都采用流水線機(jī)制加快指令的處理速度,一般來說,一條指令需要若干個(gè)CPU時(shí)鐘周期處理,而通過流水線并行執(zhí)行,可以在同等的時(shí)鐘周期內(nèi)執(zhí)行若干條指令,具體做法簡單地說就是把指令分為不同的執(zhí)行周期,例如讀取、尋址、解析、執(zhí)行等步驟,并放在不同的元件中處理,同時(shí)在執(zhí)行單元EU中,功能單元被分為不同的元件,例如加法元件、乘法元件、加載元件、存儲元件等,可以進(jìn)一步實(shí)現(xiàn)不同的計(jì)算并行執(zhí)行。
西安達(dá)內(nèi)培訓(xùn)講師表示,流水線架構(gòu)決定了指令應(yīng)該被并行執(zhí)行,而不是在順序化模型中所認(rèn)為的那樣。重排序有利于充分使用流水線,進(jìn)而達(dá)到超標(biāo)量的效果。
確保順序性
盡管指令在執(zhí)行時(shí)并不一定按照我們所編寫的順序執(zhí)行,但毋庸置疑的是,在單線程環(huán)境下,指令執(zhí)行的最終效果應(yīng)當(dāng)與其在順序執(zhí)行下的效果一致,否則這種優(yōu)化便會失去意義。
通常無論是在編譯期還是運(yùn)行期進(jìn)行的指令重排序,都會滿足上面的原則。