最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java開發(fā)CPU流水線與指令亂序執(zhí)行詳解

 更新時間:2022年09月06日 15:45:48   作者:蟬沐風(fēng)  
這篇文章主要為大家介紹了java開發(fā)CPU流水線與指令亂序執(zhí)行詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

青蛙見了蜈蚣,好奇地問:"蜈蚣大哥,我很好奇,你那么多條腿,走路的時候先邁哪一條啊?"

蜈蚣聽后說:"青蛙老弟,我一直就這么走路,從沒想過先邁哪一條腿,等我想一想再回答你。"

蜈蚣站立了幾分鐘,它一邊思考一邊向前,蹣跚了幾步,終于趴下去了。

它對青蛙說:“請你再也別問其它蜈蚣這個問題了!我一直都在這樣走路,這根本不成問題!可現(xiàn)在你問我先移動哪一條腿,我也不知道了。搞得我現(xiàn)在連路都不會走了,我該怎么辦呢?”

這個小故事屬實反映了我最近的心態(tài):

越學(xué)越不會了。。。

本來synchronizedvolatile關(guān)鍵字用得好好的,我非要深入研究一下他們的原理,所以研究了內(nèi)存屏障,又研究了和內(nèi)存屏障相關(guān)的MESI,又研究了Cache CoherenceMemory Consistency,發(fā)現(xiàn)一切問題都出在CPU身上。于是又驚嘆Java一次編寫到處運行的特性,最終又研究到JMM。

說是研究,其實就是把學(xué)習(xí)過程中自己拋出來的問題解決掉,把所有知識穿成一條線罷了。

這條線的線頭就從指令的亂序執(zhí)行開始了。

經(jīng)典的指令亂序執(zhí)行的原因有兩種,分別是Compiler ReorderingCPU Reordering

1. Compiler Reordering

編譯器會對高級語言的代碼進行分析,如果它認為你的代碼可以優(yōu)化,那么他會對你的代碼進行各種優(yōu)化然后生成匯編指令。當然,本文說的優(yōu)化主要是指令重排(Compiler Reordering)。

但是編譯器的優(yōu)化必須滿足特定的條件,一個非常重要的原則就是as-if-serial語義:

Allows any and all code transformations that do not change the observable behavior of the program.

編譯器必須遵守as-if-serial語義,也就是編譯器不會對存在數(shù)據(jù)依賴關(guān)系的操作做重排序,因為這種重排序會改變執(zhí)行結(jié)果。 但是,如果操作之間不存在數(shù)據(jù)依賴關(guān)系,這些操作就可能被編譯器和處理器重排序。

我們用非常簡單的C++代碼舉個例子(因為編譯更簡單,看起來也更直觀)。

int a,b,c;
void bar()
{
        a = c + 1;
        b = 1;
}
int main()
{
        bar();
        return 0;
}

我們對這段代碼進行變異,讓編譯器在O2級別優(yōu)化的情況下編譯代碼,我截取其中的bar()的匯編代碼,如下所示:

_Z3barv:
.LFB0:
        .cfi_startproc
        endbr64
        movl    $1, b(%rip)      #將1的值賦給b,即b = 1
        movl    c(%rip), %eax    #將c的值放到寄存器%eax中
        addl    $1, %eax         #將寄存器%eax的值+1,即c + 1
        movl    %eax, a(%rip)    #將寄存器%eax的值賦給a,即a = c + 1
        ret

我們發(fā)現(xiàn),編譯得到的匯編代碼和我們原本的C語言代碼順序并不一致。

匯編指令先執(zhí)行了b = 1,之后才執(zhí)行了a = c + 1。說明變量abstore操作并沒有按照他們在程序中定義的順序來執(zhí)行。

既然匯編指令被重排了,CPU的執(zhí)行順序自然是根據(jù)匯編指令對應(yīng)的機器指令執(zhí)行的,大概率也會被重排。其實除此之外,CPU本身也會對指令進行重排(CPU Reordering)。

2. CPU 流水線

談及處理器必談及流水線,處理器的流水線結(jié)構(gòu)是處理器微架構(gòu)最基本的一個要素,也是造成CPU Reordering的主要因素。

2.1. 從汽車裝配談起

流水線的概念始于工業(yè)制造領(lǐng)域,但是鑒于大部分人其實都沒接觸過流水線,我們不妨舉一個汽車生產(chǎn)的例子來解釋流水線的誕生。

我們首先粗淺地認為汽車的裝配需要兩個步驟:

  • 制作零件:制作車身外殼、發(fā)動機和各種其他部件;
  • 組裝:將各零部件(自己制作和外采的所有零部件)組裝成車。

假設(shè)一個工人進行每個步驟都占用1個月,如果不采用流水線,而采用串行方式來執(zhí)行的話,一年時間可以裝配6輛汽車,過程見下圖:

串行的效率實在是太有限了,根本原因就是裝配的兩個步驟都是由一個人完成的。如果有人能在組裝進行的同時制作零件,效率會大大提升,也就是每個流程只專注一件事情,我們再引入一個工人。

這樣一個人專門負責(zé)制作零件,另一個人專門組裝零件,兩個工作交疊進行,過程見下圖:

增加一個人手之后,除了第一個月,每一個月都有完整的制作零件和組裝流程,因此一年內(nèi)可以完成11臺汽車的裝配(相比于串行方式的6臺,幾乎翻倍了),從第二年開始,每年就能裝配12臺了(直接翻倍)。

這個過程就是流水線的執(zhí)行過程,因為我們把汽車的制作過程分成了兩個步驟,因此以上流水線成為二級流水線。

我們繼續(xù)優(yōu)化,我們將制作零件的步驟分成時間周期更短的沖壓和焊接兩步,將組裝步驟分為時間周期更短的涂裝和總裝兩步,并且假設(shè)每個步驟的時間周期為0.5個月。

當然嘍,我們得再雇傭倆人。

現(xiàn)在就是四級流水線了,神奇的事情發(fā)生了,四級流水線使得原本需要一年時間的任務(wù)現(xiàn)在只需要4.5個月便可以完成,再次提升了效率。如下圖所示:

2.2. 現(xiàn)代CPU的流水線

現(xiàn)代 CPU 支持多級指令流水線,例如支持同時執(zhí)行 取指令 - 指令譯碼 - 執(zhí)行指令 - 內(nèi)存訪問 - 數(shù)據(jù)寫回的處理器,就可以稱之為五級指令流水線。

這時 CPU 可以在一個時鐘周期內(nèi),同時運行五條指令的不同階段,其中每個階段的都占用一個或多個指令周期(CPU以執(zhí)行時間最長),本質(zhì)上,流水線技術(shù)井不能縮短單條指令的執(zhí)行時間,但它變相地提高了指令的吞吐率。

上面的CPU流水線圖并非特定型號的CPU的示例,而是為了說明幾個問題特意畫成了這個樣子。

  • 通常而言,CPU設(shè)計者會選擇執(zhí)行時間最長的流水線階段作為一個時鐘周期,這樣能保證其他階段能在一個時鐘周期內(nèi)完成,避免出現(xiàn)流水線斷流。
  • 每一個流水線級的時間都是一個時鐘周期,但是其中實際操作的時間,可能短于一個時鐘周期。比如譯碼器其實就是一個組合邏輯電路,門延遲很低,就不需要一個完整的時鐘周期就能完成自己的任務(wù),任務(wù)完成之后CPU其實是在“等待”。

很多人可能會問,既然流水線這么好用,那為什么CPU設(shè)計者不設(shè)計一個超長流水線呢?這就需要說明一下超長流水線的瓶頸了。

3. 超長流水線的瓶頸

3.1. 性能瓶頸

流水線長度的增加,是有性能成本的。

每一級流水線的輸出都需要放在流水線寄存器中,然后再下一個時鐘周期,交給下一個流水線級去處理。每增加一級流水線,就要多一級寫入流水線寄存器的操作。

以多線程為例,數(shù)量合適的多線程會提高數(shù)據(jù)的處理速度,但是當線程數(shù)量太多,線程之間的時間切換成本就無法被忽視,線程的增加甚至可能成為性能提升的負擔(dān)。

3.2. 功耗瓶頸

提升流水線的深度,需要同步提高CPU的主頻。再看一下這個圖:

由于流水線的每一級被分得特別細,甚至有的還沒有完全占滿單個時鐘周期,也就意味著單個時鐘周期內(nèi)能完成的事情變少了,因此只有提升主頻,CPU 在指令的響應(yīng)時間這個指標上才能保持和原來相同的性能。

提升主頻和流水線深度就以為這晶體管的增加,也就以為這功耗變大。

沒人想擁有一臺“充電3小時,辦公20分鐘”的一臺筆記本電腦吧。

3.3. 指令亂序

還是以上面的圖為例(就不再貼一遍了),指令1的訪存操作使用了多個時鐘周期,導(dǎo)致指令2和指令3在指令1之前完成了。

如果是一般的代碼還好,但如果是具有依賴性的代碼,比如:

float a = 3.14159 * 0.2; // 指令1
float b = a * 2;         // 指令2
float c = b + 1;         // 指令3
float d = 10;            // 指令4

指令1、2、3的執(zhí)行順序就絕不能向圖中表示的那樣亂序執(zhí)行。其中有兩點需要我們注意:

  • 由于上圖中情形的存在,導(dǎo)致CPU確實有可能出現(xiàn)亂序執(zhí)行的情況;
  • CPU需要阻止具有依賴關(guān)系的指令亂序執(zhí)行(指令1,2,3),轉(zhuǎn)而讓后續(xù)沒有依賴關(guān)系的指令(指令4)先執(zhí)行。

對于第2條,如果流水線只有5級還好說,CPU自然有辦法判斷哪些指令具有依賴性,并拒絕做出指令亂序。但是如果有20條流水線,CPU肯定還有辦法判斷,但是可想而知,這種判斷勢必會影響CPU的性能。

回到本文一開始說的編譯器指令重排序,當然嘍,也包含Java的JIT將字節(jié)碼編譯成機器碼時的指令重排序,就是為了把沒有依賴關(guān)系的指令放一起,本質(zhì)上都是為了適配CPU,更好地發(fā)揮出CPU流水線的功能,從而提升性能罷了。

4. 總結(jié)

說了這么多,很可能在我之后的文章中被一句話帶過。

其實我想表達的思想就是,實際代碼運行的順序可能和我們代碼編寫的順序并不一致。記住這句話很容易,但或許總會有人像我一樣想稍微深入一點來了解這句話的本質(zhì)吧。

除了本文所述,CPU和高速緩存之間的交互過程中,硬件工程師也著實給軟件開發(fā)者挖了不少坑,內(nèi)存屏障就是在這種背景下產(chǎn)生的。

以上就是java開發(fā)CPU流水線與指令亂序執(zhí)行詳解的詳細內(nèi)容,更多關(guān)于java CPU流水線指令亂序執(zhí)行的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • spring cloud 分布式鏈路追蹤的方法

    spring cloud 分布式鏈路追蹤的方法

    這篇文章主要介紹了spring cloud 分布式鏈路追蹤的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-07-07
  • 一次由Lombok的@AllArgsConstructor注解引發(fā)的錯誤及解決

    一次由Lombok的@AllArgsConstructor注解引發(fā)的錯誤及解決

    這篇文章主要介紹了一次由Lombok的@AllArgsConstructor注解引發(fā)的錯誤及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java StringBuffer與StringBuilder有什么區(qū)別

    Java StringBuffer與StringBuilder有什么區(qū)別

    當對字符串進行修改的時候,需要使用 StringBuffer 和 StringBuilder類,和String類不同的是,StringBuffer和 StringBuilder類的對象能夠被多次的修改,并且不產(chǎn)生新的未使用對象,本篇我們來分析分析它們的區(qū)別
    2023-01-01
  • 一文理解kafka?rebalance負載均衡

    一文理解kafka?rebalance負載均衡

    這篇文章主要為大家介紹了kafka?rebalance負載均衡的深入理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • mybatis攔截器無法注入spring bean的問題解決

    mybatis攔截器無法注入spring bean的問題解決

    本文主要介紹了mybatis攔截器無法注入spring bean的問題解決,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-02-02
  • 通過實例學(xué)習(xí)Java集合框架HashSet

    通過實例學(xué)習(xí)Java集合框架HashSet

    這篇文章主要介紹了通過實例學(xué)習(xí)Java集合框架HashSet,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-12-12
  • Springboot使用異步請求提高系統(tǒng)的吞吐量詳解

    Springboot使用異步請求提高系統(tǒng)的吞吐量詳解

    這篇文章主要介紹了Springboot使用異步請求提高系統(tǒng)的吞吐量詳解,和同步請求相對,異步不需要等待響應(yīng),隨時可以發(fā)送下一次請求,如果是同步請求,需要將信息填寫完整,再發(fā)送請求,服務(wù)器響應(yīng)填寫是否正確,再做修改,需要的朋友可以參考下
    2023-08-08
  • controller層如何同時接收兩個實體類

    controller層如何同時接收兩個實體類

    這篇文章主要介紹了controller層如何同時接收兩個實體類問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Java中實現(xiàn)Comparator接口和用法實例(簡明易懂)

    Java中實現(xiàn)Comparator接口和用法實例(簡明易懂)

    這篇文章主要介紹了Java中實現(xiàn)Comparator接口和用法實例(簡明易懂),本文給出實現(xiàn)Comparator接口的實例和使用這個接口的代碼實例,需要的朋友可以參考下
    2015-05-05
  • Java如何實現(xiàn)數(shù)字逆序

    Java如何實現(xiàn)數(shù)字逆序

    這篇文章主要介紹了Java如何實現(xiàn)數(shù)字逆序問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-04-04

最新評論

赞皇县| 枣阳市| 盐山县| 安顺市| 乐清市| 眉山市| 和平县| 盐津县| 方山县| 桐柏县| 乌海市| 尼勒克县| 登封市| 南郑县| 城固县| 弥渡县| 凌源市| 和龙市| 县级市| 新沂市| 额济纳旗| 蓝山县| 久治县| 曲麻莱县| 郧西县| 高唐县| 蒙城县| 河北区| 博罗县| 蚌埠市| 凤山市| 元阳县| 诏安县| 武川县| 巢湖市| 安西县| 武鸣县| 九龙县| 疏勒县| 广宁县| 延庆县|