国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

為什么要指令重排序?

Eastboat / 3480人閱讀

摘要:那么我們就應(yīng)該問問為啥要用指令重排序呢生活類比我們從生活中舉個例子,假設(shè)你有一箱紅紙,現(xiàn)在要你剪成小紅花貼在窗上。

我們知道java在運(yùn)行的時(shí)候有兩個地方可能用到重排序,一個是編譯器編譯的的時(shí)候,一個是處理器運(yùn)行的時(shí)候。
那么我們就應(yīng)該問問為啥要用指令重排序呢?

生活類比

我們從生活中舉個例子,假設(shè)你有一箱紅紙,現(xiàn)在要你剪成小紅花貼在窗上。你有兩種極端的選擇:拿出來一個,把這個剪好,再貼上去......一個一個依次進(jìn)行;另一種方式是先全部拿出來,然后全部剪好,最后全部貼上去。

那種效率更高?很明顯是后者,因?yàn)榍罢吣憔托枰煌5卦谙渥樱舻逗湍z水之間切換,這個切換過程不僅浪費(fèi)時(shí)間,還耗費(fèi)精力。但是后者一直做一個工作也很無聊,還會導(dǎo)致半天了窗上一朵花都沒有,會給你帶來失落感,所以比較合適的做法就是拿出來一疊,把這一疊剪好,貼上去。這樣既不無聊,也減少了切換次數(shù),提高了工作效率。

再想想,如果有三個人,一個負(fù)責(zé)拿,一個負(fù)責(zé)剪,一個負(fù)責(zé)貼,就更快了。

分析

編譯期重排序有啥好處?CPU計(jì)算的時(shí)候要訪問值,如果常常利用到寄存器中已有的值就不用去內(nèi)存讀取了,比如說

int a = 1;
int b = 1;
a = a + 1;
b = b +1 ;

就可能沒有

int a = 1;
a = a + 1;
int b = 1;
b = b +1 ;

性能好,因?yàn)楹笳呖梢?a或b可能在寄存器中了。

處理器為啥要重排序?因?yàn)橐粋€匯編指令也會涉及到很多步驟,每個步驟可能會用到不同的寄存器,CPU使用了流水線技術(shù),也就是說,CPU有多個功能單元(如獲取、解碼、運(yùn)算和結(jié)果),一條指令也分為多個單元,那么第一條指令執(zhí)行還沒完畢,就可以執(zhí)行第二條指令,前提是這兩條指令功能單元相同或類似,所以一般可以通過指令重排使得具有相似功能單元的指令接連執(zhí)行來減少流水線中斷的情況。

我們寫一段代碼來試試:

package *****;

/**
 * reorder
 * @author Mageek Chiu
 * @date 2018/5/25 0025:12:49
 */
public class ReOrder {

    public int value ;

    private ReOrder(int value) {
        this.value = value;
    }

    public static void main(String... args){
        ReOrder reOrder = new ReOrder(111);
        ReOrder reOrder1 = new ReOrder(222);
        ReOrder reOrder2 = new ReOrder(333);
        System.out.println(add1(reOrder,reOrder1,reOrder2));
    }

    static int add1(ReOrder reOrder,ReOrder reOrder1,ReOrder reOrder2){
        int result = 0;

        result += reOrder.value;
        result += reOrder1.value;
        result += reOrder2.value;//***

        result += reOrder.value;
        result += reOrder1.value;
        result += reOrder2.value;

        result += reOrder.value;
        result += reOrder1.value;
        result += reOrder2.value;

        return result;

    }

}

運(yùn)行結(jié)果中:

 # {method} {0x000000001c402c80} "add1" "(*****/ReOrder;*****/ReOrder;*****/ReOrder;)I" in "*****/ReOrder"
  # parm0:    rdx:rdx   = "*****/ReOrder"
  # parm1:    r8:r8     = "*****/ReOrder"
  # parm2:    r9:r9     = "*****/ReOrder"
  #           [sp+0x20]  (sp of caller)
  0x00000000032a86c0: mov     dword ptr [rsp+0ffffffffffffa000h],eax
  0x00000000032a86c7: push    rbp
  0x00000000032a86c8: sub     rsp,10h           ;*synchronization entry
                                                ; - *****.ReOrder::add1@-1 (line 24)

  0x00000000032a86cc: mov     r11d,dword ptr [rdx+0ch]
                                                ;*getfield value
                                                ; - *****.ReOrder::add1@4 (line 26)
                                                ; implicit exception: dispatches to 0x00000000032a86ff
  0x00000000032a86d0: mov     r10d,dword ptr [r8+0ch]  ;*getfield value
                                                ; - *****.ReOrder::add1@11 (line 27)
                                                ; implicit exception: dispatches to 0x00000000032a870d
  0x00000000032a86d4: mov     r9d,dword ptr [r9+0ch]  ;*getfield value
                                                ; - *****.ReOrder::add1@18 (line 28)
                                                ; implicit exception: dispatches to 0x00000000032a8719
  0x00000000032a86d8: mov     eax,r11d
  0x00000000032a86db: add     eax,r10d
  0x00000000032a86de: add     eax,r9d
  0x00000000032a86e1: add     eax,r11d
  0x00000000032a86e4: add     eax,r10d
  0x00000000032a86e7: add     eax,r9d
  0x00000000032a86ea: add     eax,r11d
  0x00000000032a86ed: add     eax,r10d
  0x00000000032a86f0: add     eax,r9d           ;*iadd

也就是先用mov把方法里面所需要的三個value加載了,再統(tǒng)一用add進(jìn)行加法運(yùn)算。

現(xiàn)在我們把//***哪一行注釋掉,運(yùn)行結(jié)果如下:

[Constants]
  # {method} {0x000000001c052c78} "add1" "(*****/ReOrder;*****/ReOrder;*****/ReOrder;)I" in "*****/ReOrder"
  # parm0:    rdx:rdx   = "*****/ReOrder"
  # parm1:    r8:r8     = "*****/ReOrder"
  # parm2:    r9:r9     = "*****/ReOrder"
  #           [sp+0x20]  (sp of caller)
  0x0000000002f47d40: mov     dword ptr [rsp+0ffffffffffffa000h],eax
  0x0000000002f47d47: push    rbp
  0x0000000002f47d48: sub     rsp,10h           ;*synchronization entry
                                                ; - *****.ReOrder::add1@-1 (line 24)

  0x0000000002f47d4c: mov     r11d,dword ptr [rdx+0ch]
                                                ;*getfield value
                                                ; - *****r.ReOrder::add1@4 (line 26)
                                                ; implicit exception: dispatches to 0x0000000002f47d7c
  0x0000000002f47d50: mov     r10d,dword ptr [r8+0ch]  ;*getfield value
                                                ; - *****.ReOrder::add1@11 (line 27)
                                                ; implicit exception: dispatches to 0x0000000002f47d89
  0x0000000002f47d54: mov     r9d,dword ptr [r9+0ch]  ;*getfield value
                                                ; - *****::add1@32 (line 32)
                                                ; implicit exception: dispatches to 0x0000000002f47d95
  0x0000000002f47d58: mov     eax,r11d
  0x0000000002f47d5b: add     eax,r10d
  0x0000000002f47d5e: add     eax,r11d
  0x0000000002f47d61: add     eax,r10d
  0x0000000002f47d64: add     eax,r9d
  0x0000000002f47d67: add     eax,r11d
  0x0000000002f47d6a: add     eax,r10d
  0x0000000002f47d6d: add     eax,r9d           ;*iadd

依然是先把所有value都用mov指令加載后再進(jìn)行加法運(yùn)算。
總結(jié)起來就是不管代碼里這個值使用順序多靠后,都先用mov加載后再使用add對這個值進(jìn)行運(yùn)算。

注意,上面的運(yùn)行參數(shù)為-Xcomp -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*ReOrder.add1 -XX:+PrintCompilation
Xcomp 含義是使用編譯模式而不是解釋模式, -XX:CompileCommand=print,*ReOrder.add1表示只打印這個方法,-XX:+PrintCompilation表示打印方法名稱。
需要插件hsdis,編譯好后放在jdk的jre的bin的server中就好,具體環(huán)境搭建可以參閱這里

分析不對的地方請輕拍。
訪問原文

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/69559.html

相關(guān)文章

  • Java并發(fā)編程之指令重排

    摘要:安全性小結(jié)我們上邊介紹了原子性操作內(nèi)存可見性以及指令重排序三個在多線程執(zhí)行過程中會影響到安全性的問題。 指令重排序 如果說內(nèi)存可見性問題已經(jīng)讓你抓狂了,那么下邊的這個指令重排序的事兒估計(jì)就要罵娘了~這事兒還得從一段代碼說起: public class Reordering { private static boolean flag; private static in...

    microcosm1994 評論0 收藏0
  • 從JVM并發(fā)看CPU內(nèi)存指令重排

    摘要:處理器通過緩存能夠從數(shù)量級上降低內(nèi)存延遲的成本這些緩存為了性能重新排列待定內(nèi)存操作的順序。從上述觸發(fā)步驟中,可以看到第步發(fā)生了指令重排序,并導(dǎo)致第步讀到錯誤的數(shù)據(jù)。內(nèi)存屏障是用來防止出現(xiàn)指令重排序的利器之一。 這兩天,我拜讀了 Dennis Byrne 寫的一片博文Memory Barriers and JVM Concurrency (中譯文內(nèi)存屏障與JVM并發(fā))。 文中提到: ...

    vboy1010 評論0 收藏0
  • 《深入理解 Java 內(nèi)存模型》讀書筆記

    摘要:前提深入理解內(nèi)存模型程曉明著,該書在以前看過一遍,現(xiàn)在學(xué)的東西越多,感覺那塊越重要,于是又再細(xì)看一遍,于是便有了下面的讀書筆記總結(jié)。同步同步是指程序用于控制不同線程之間操作發(fā)生相對順序的機(jī)制。線程之間的通信由內(nèi)存模型控制。 showImg(https://segmentfault.com/img/remote/1460000013474312?w=1920&h=1271); 前提 《深...

    xuexiangjys 評論0 收藏0
  • 深入理解Java內(nèi)存模型(一)——基礎(chǔ)

    摘要:線程之間的通信由內(nèi)存模型本文簡稱為控制,決定一個線程對共享變量的寫入何時(shí)對另一個線程可見。為了保證內(nèi)存可見性,編譯器在生成指令序列的適當(dāng)位置會插入內(nèi)存屏障指令來禁止特定類型的處理器重排序。 并發(fā)編程模型的分類 在并發(fā)編程中,我們需要處理兩個關(guān)鍵問題:線程之間如何通信及線程之間如何同步(這里的線程是指并發(fā)執(zhí)行的活動實(shí)體)。通信是指線程之間以何種機(jī)制來交換信息。在命令式編程中,線程之間的...

    jsdt 評論0 收藏0
  • 《深入理解 Java 內(nèi)存模型》讀書筆記

    摘要:前提深入理解內(nèi)存模型程曉明著,該書在以前看過一遍,現(xiàn)在學(xué)的東西越多,感覺那塊越重要,于是又再細(xì)看一遍,于是便有了下面的讀書筆記總結(jié)。同步同步是指程序用于控制不同線程之間操作發(fā)生相對順序的機(jī)制。線程之間的通信由內(nèi)存模型控制。 showImg(https://mmbiz.qpic.cn/mmbiz_jpg/1flHOHZw6RtPu3BNx3zps1JhSmPICRw7QgeOmxOfTb...

    姘存按 評論0 收藏0
  • 什么雙重檢查鎖模式需 volatile ?

    摘要:注意,禁止指令重排序在之后才被修復(fù)使用局部變量優(yōu)化性能重新查看中雙重檢查鎖定代碼。幫助文檔雙重檢查鎖定與延遲初始化有關(guān)雙重檢查鎖定失效的說明 雙重檢查鎖定(Double check locked)模式經(jīng)常會出現(xiàn)在一些框架源碼中,目的是為了延遲初始化變量。這個模式還可以用來創(chuàng)建單例。下面來看一個 Spring 中雙重檢查鎖定的例子。 showImg(https://segmentfaul...

    geekzhou 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<