<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Apache Doris在作業(yè)幫實(shí)時(shí)數(shù)倉(cāng)中的應(yīng)用實(shí)踐

          共 6302字,需瀏覽 13分鐘

           ·

          2020-10-25 17:44

          點(diǎn)擊上方藍(lán)色字體,選擇“設(shè)為星標(biāo)

          回復(fù)”資源“獲取更多資源

          大數(shù)據(jù)技術(shù)與架構(gòu)
          點(diǎn)擊右側(cè)關(guān)注,大數(shù)據(jù)開(kāi)發(fā)領(lǐng)域最強(qiáng)公眾號(hào)!

          大數(shù)據(jù)真好玩
          點(diǎn)擊右側(cè)關(guān)注,大數(shù)據(jù)真好玩!

          ?1. 什么是空檢查

          在Java里經(jīng)常會(huì)判斷一個(gè)對(duì)象是否為空,如果為空的對(duì)象訪問(wèn)方法,字段會(huì)拋出空指針異常,而空指針異常為運(yùn)行異常,如果不抓取這個(gè)異常,有的時(shí)候會(huì)導(dǎo)致程序異常,為了解決這個(gè)問(wèn)題,我們通常會(huì)在代碼里顯式的去判斷該對(duì)象是否為空,進(jìn)行為空的邏輯處理,這種做法邏輯雖然明確,但是由于空的邏輯并不是經(jīng)常碰到,這樣會(huì)導(dǎo)致有多余的邏輯分支判斷。

          2. 隱式空檢查 implicit exception

          我們先來(lái)看一個(gè)代碼:

          public static int nullCheck(String value) {
          if(value == null){
          return -1;
          }
          else{
          return value.length();
          }
          }

          我們進(jìn)行運(yùn)行編譯獲取編譯后的匯編

            0x00007f23c922f107: mov    0xc(%rsi),%eax     ; implicit exception: code begin: 0x00007f23c922f107; code end: 0x00007f23c922f10a; code end: 0x00007f23c922f0e0; implicit exception: dispatches to 0x00007f23c922f1a1
          0x00007f23c922f10a: push %r10
          0x00007f23c922f10c: cmp 0x15deda15(%rip),%r12 # 0x00007f23df01cb28

          我們并沒(méi)有看到有邏輯分支對(duì)value.length中的value進(jìn)行空指針判斷,我們?cè)谂赃叺淖⑨屩锌吹搅藰?gòu)建了Implicit Exception的跳轉(zhuǎn)地址 implicit exception: dispatches to 0x00007f23c922f1a1 mov 0xc(%rsi),%eax這個(gè)指令并不是一個(gè)跳轉(zhuǎn)指令,但為何在旁邊的代碼注釋中卻標(biāo)明了Implicit Exception呢?這是因?yàn)樵贘ava編譯的過(guò)程中會(huì)生成一段ImplicitNullCheckStub代碼,用來(lái)處理遇到Null的場(chǎng)景。

           ;; ImplicitNullCheckStub slow case
          0x00007f23c922f1a1: callq 0x00007f23c9166460 ; OopMap{off=198}
          ;*invokevirtual length
          ; - NullCheck::hotMethod@7 (line 33)
          ; {runtime_call}
          0x00007f23c922f1a6: mov %rsp,-0x28(%rsp)
          0x00007f23c922f1ab: sub $0x80,%rsp
          0x00007f23c922f1b2: mov %rax,0x78(%rsp)
          0x00007f23c922f1b7: mov %rcx,0x70(%rsp)
          0x00007f23c922f1bc: mov %rdx,0x68(%rsp)
          0x00007f23c922f1c1: mov %rbx,0x60(%rsp)
          0x00007f23c922f1c6: mov %rbp,0x50(%rsp)
          0x00007f23c922f1cb: mov %rsi,0x48(%rsp)
          0x00007f23c922f1d0: mov %rdi,0x40(%rsp)
          0x00007f23c922f1d5: mov %r8,0x38(%rsp)
          0x00007f23c922f1da: mov %r9,0x30(%rsp)
          0x00007f23c922f1df: mov %r10,0x28(%rsp)
          0x00007f23c922f1e4: mov %r11,0x20(%rsp)
          0x00007f23c922f1e9: mov %r12,0x18(%rsp)
          0x00007f23c922f1ee: mov %r13,0x10(%rsp)
          0x00007f23c922f1f3: mov %r14,0x8(%rsp)
          0x00007f23c922f1f8: mov %r15,(%rsp)
          0x00007f23c922f1fc: movabs $0x7f23de9c944b,%rdi ; {external_word}
          0x00007f23c922f206: movabs $0x7f23c922f1a6,%rsi ; {internal_word}
          0x00007f23c922f210: mov %rsp,%rdx
          0x00007f23c922f213: and $0xfffffffffffffff0,%rsp
          0x00007f23c922f217: callq 0x00007f23de53c7a0 ; {runtime_call}
          0x00007f23c922f21c: hlt

          那什么時(shí)候會(huì)觸發(fā)ImplicitNullCheckStub的調(diào)用呢?因?yàn)镸ov指令當(dāng)碰到無(wú)效地址的時(shí)候,在Linux系統(tǒng)中會(huì)產(chǎn)生一個(gè)發(fā)生signalled exception(在這種情況下是SIGSEGV),這時(shí)候會(huì)轉(zhuǎn)到信號(hào)處理函數(shù),如果應(yīng)用有自定義的該信號(hào)處理函數(shù),就執(zhí)行該信號(hào)處理函數(shù)。JVM在linux下注冊(cè)了JVM_handle_linux_signal函數(shù)

          else if (sig == SIGSEGV &&
          !MacroAssembler::needs_explicit_null_check((intptr_t)info->si_addr)) {
          // Determination of interpreter/vtable stub/compiled code null exception
          stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::IMPLICIT_NULL);
          }

          在continuation_for_implicit_exception函數(shù)里,通過(guò)當(dāng)前異常地址獲取target_pc = nm->continuation_for_implicit_exception(pc);地址,把地址內(nèi)容保存到信號(hào)處理函數(shù)的context中

            if (stub != NULL) {
          // save all thread context in case we need to restore it
          if (thread != NULL) thread->set_saved_exception_pc(pc);

          uc->uc_mcontext.gregs[REG_PC] = (greg_t)stub;
          return true;
          }

          由linux的信號(hào)處理來(lái)跳轉(zhuǎn)到指定的stub中,也就是ImplicitNullCheckStub

          在這里我們看到JVM并沒(méi)有顯示的增加指令分支對(duì)Null進(jìn)行檢查,而是通過(guò)異常信號(hào)處理機(jī)制來(lái)處理,跳轉(zhuǎn)到ImplicitNullCheckStub里單獨(dú)處理這里是有性能的損耗,為何JVM里會(huì)考慮使用異常信號(hào)處理機(jī)制,是因?yàn)榭紤]到大部分的場(chǎng)景不為空,提高執(zhí)行效率的一種方式。

          3. C1的Null Eliminator

          C1的Null Eliminator 于C2不太一樣, C1 的Null Eliminator 解決的是重復(fù)check null的問(wèn)題。

          整體思路:

          顯式的調(diào)用Nullcheck的時(shí)候,需要將顯式的NullCheck擦除,改成ImplicitNullCheck 對(duì)同一個(gè)參數(shù)使用不需要每次都引入null檢查,只要在第一次檢查后,后續(xù)就可以將null檢查給插除了。算法:數(shù)據(jù)流分析 OUT[entry] = ?; for (each basic block B\entry) { IN[B] = U P a predecessor of B OUT[P]; if (changes to IN occur)){ OUT[B] = genB U (IN[B]); } }

          C1是使用SSA的表達(dá)方式,我們會(huì)發(fā)現(xiàn)沒(méi)有了傳統(tǒng)流分析算法里的Kill函數(shù),在SSA里的use-define鏈路里如果一個(gè)參數(shù)如果進(jìn)行redfine過(guò)后,參數(shù)的命名會(huì)變化,在使用的時(shí)候就已經(jīng)使用新的參數(shù)名字,這樣就天生具備了kill的能力。

          我們先來(lái)看一個(gè)SSA的例子:

          . 18   0    a13    null_check(a3)
          . 1 0 a16 a3._12 ([) value
          . 4 0 i17 a16.length
          . 21 0 i19 ireturn i17

          Null Eliminator 分析的是value,在上面的第一行首先現(xiàn)有Null_Check,這是在調(diào)用函數(shù)的時(shí)候,IR層添加了null_check,根據(jù)算法我們會(huì)顯示的去除null_check a3 并設(shè)置為implicit null檢查,而對(duì)第二句語(yǔ)句 a16 使用了a3 并且又跟在a13語(yǔ)句后面,故而可以直接使用第一個(gè)語(yǔ)句的implicit null check,而把第一個(gè)語(yǔ)句null check 徹底的擦除,假如后續(xù)的語(yǔ)句繼續(xù)使用a3的化,那么該語(yǔ)句的implicit null check 就可以直接擦除了。

          算法其實(shí)和常見(jiàn)的流分析一樣,設(shè)置一個(gè)ValueSet,對(duì)每個(gè)參數(shù)的下標(biāo)以bit位置來(lái)保存,同時(shí)每一個(gè)Block都會(huì)保存一個(gè)ValueSet

          算法實(shí)現(xiàn)細(xì)節(jié):

          • Null Eliminator 是一個(gè)前向分析

          • 分析流從不同的BB塊流向的時(shí)候,每個(gè)Block都會(huì)Uion 上一個(gè)Block塊ValueSet

          • 如果發(fā)現(xiàn)變化,就會(huì)對(duì)Block里的指令進(jìn)行遍歷分析

          • 分析指令里的Value參數(shù)

          • 該參數(shù)已經(jīng)在bitset里被設(shè)置過(guò),就代表已經(jīng)做過(guò)Null check

          • 如果前面的指令做的是顯式的null check,那么插除的就是顯示的null check,補(bǔ)上Implicit null check

          • 如果前面的指令做的是Implicit null check,那么該null check將會(huì)被Eliminator

          3.1 Null Check Eliminator

            void handle_AccessField     (AccessField* x);
          void handle_ArrayLength (ArrayLength* x);
          void handle_LoadIndexed (LoadIndexed* x);
          void handle_StoreIndexed (StoreIndexed* x);
          void handle_NullCheck (NullCheck* x);
          void handle_Invoke (Invoke* x);
          void handle_NewInstance (NewInstance* x);
          void handle_NewArray (NewArray* x);
          void handle_AccessMonitor (AccessMonitor* x);
          void handle_Intrinsic (Intrinsic* x);
          void handle_ExceptionObject (ExceptionObject* x);
          void handle_Phi (Phi* x);
          void handle_ProfileCall (ProfileCall* x);
          void handle_ProfileReturnType (ProfileReturnType* x);

          在上面函數(shù)里定義的我們可以看到訪問(wèn)field, array, 顯示的null check, 調(diào)用, 初始化對(duì)象,異常對(duì)象,以及phi函數(shù) 我們?yōu)檫@里單獨(dú)的討論一下phi函數(shù):關(guān)于Phi函數(shù)是什么,在這里我們就不介紹了:先來(lái)看一段IR

          B2 (V) [22, 31] pred: B10 B1
          Locals:
          0 a3
          1 a18 [ a4 a10]


          empty stack
          inlining depth 0
          __bci__use__tid____instr____________________________________
          . 23 0 i19 a18._12 (I) x
          . 27 0 a20 null_check(a3)
          . 1 0 a23 a3._12 ([) value
          . 4 0 i24 a23.length
          30 0 i26 i19 + i24
          . 31 0 i27 ireturn i26

          我們可以看到a18 phi參數(shù)里面決定的是a4 a10。分析Phi函數(shù)需要分析a4, a10,如果a4, a10都已經(jīng)進(jìn)行空檢查過(guò),那么該a18也就可以進(jìn)行null Eliminator

          3.2 C2 Null 優(yōu)化

          C2的null優(yōu)化和C1的優(yōu)化是不一樣的,C2的Null優(yōu)化會(huì)優(yōu)化Block,通過(guò)Profile可以推斷分支是否會(huì)被執(zhí)行,如果不會(huì)被執(zhí)行,分支將會(huì)被剪支。但如果發(fā)現(xiàn)剪支錯(cuò)誤,會(huì)進(jìn)行反優(yōu)化,重新回到解釋。

          但是C1是不會(huì)的,C1的優(yōu)化并不會(huì)剪支,當(dāng)程序碰到大量的Null的時(shí)候,會(huì)執(zhí)行implicit的分支,從而大大降低效率,這里需要人工的去判斷,究竟是Null多 還是非Null多,如果Null多的化,還是建議代碼里添加null 的檢查,避免效率的大大降低。

          版權(quán)聲明:

          本文為大數(shù)據(jù)技術(shù)與架構(gòu)整理,原作者獨(dú)家授權(quán)。未經(jīng)原作者允許轉(zhuǎn)載追究侵權(quán)責(zé)任。
          編輯|冷眼丶
          微信公眾號(hào)|import_bigdata


          歡迎點(diǎn)贊+收藏+轉(zhuǎn)發(fā)朋友圈素質(zhì)三連



          文章不錯(cuò)?點(diǎn)個(gè)【在看】吧!??

          瀏覽 54
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  豆花视频成人入口网站 | 日韩mv国产视频 | 国产一级a毛一级a毛片视频黑人 | 美女任你操 | 欧美操黑人 |