1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Apache Doris在作業(yè)幫實(shí)時(shí)數(shù)倉中的應(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ù)開發(fā)領(lǐng)域最強(qiáng)公眾號!

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

        ?1. 什么是空檢查

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

        2. 隱式空檢查 implicit exception

        我們先來看一個(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

        我們并沒有看到有邏輯分支對value.length中的value進(jìn)行空指針判斷,我們在旁邊的注釋中看到了構(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編譯的過程中會(huì)生成一段ImplicitNullCheckStub代碼,用來處理遇到Null的場景。

         ;; 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)碰到無效地址的時(shí)候,在Linux系統(tǒng)中會(huì)產(chǎn)生一個(gè)發(fā)生signalled exception(在這種情況下是SIGSEGV),這時(shí)候會(huì)轉(zhuǎn)到信號處理函數(shù),如果應(yīng)用有自定義的該信號處理函數(shù),就執(zhí)行該信號處理函數(shù)。JVM在linux下注冊了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ù)里,通過當(dāng)前異常地址獲取target_pc = nm->continuation_for_implicit_exception(pc);地址,把地址內(nèi)容保存到信號處理函數(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的信號處理來跳轉(zhuǎn)到指定的stub中,也就是ImplicitNullCheckStub

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

        3. C1的Null Eliminator

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

        整體思路:

        顯式的調(diào)用Nullcheck的時(shí)候,需要將顯式的NullCheck擦除,改成ImplicitNullCheck 對同一個(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)沒有了傳統(tǒng)流分析算法里的Kill函數(shù),在SSA里的use-define鏈路里如果一個(gè)參數(shù)如果進(jìn)行redfine過后,參數(shù)的命名會(huì)變化,在使用的時(shí)候就已經(jīng)使用新的參數(shù)名字,這樣就天生具備了kill的能力。

        我們先來看一個(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檢查,而對第二句語句 a16 使用了a3 并且又跟在a13語句后面,故而可以直接使用第一個(gè)語句的implicit null check,而把第一個(gè)語句null check 徹底的擦除,假如后續(xù)的語句繼續(xù)使用a3的化,那么該語句的implicit null check 就可以直接擦除了。

        算法其實(shí)和常見的流分析一樣,設(shè)置一個(gè)ValueSet,對每個(gè)參數(shù)的下標(biāo)以bit位置來保存,同時(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ì)對Block里的指令進(jìn)行遍歷分析

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

        • 該參數(shù)已經(jīng)在bitset里被設(shè)置過,就代表已經(jīng)做過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ù)里定義的我們可以看到訪問field, array, 顯示的null check, 調(diào)用, 初始化對象,異常對象,以及phi函數(shù) 我們?yōu)檫@里單獨(dú)的討論一下phi函數(shù):關(guān)于Phi函數(shù)是什么,在這里我們就不介紹了:先來看一段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)行空檢查過,那么該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,通過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é)任。
        編輯|冷眼丶
        微信公眾號|import_bigdata


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



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

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            黄色一级片 | 综合成人 | 成人性爱视频在线观看 | 亚洲天堂精品视频 | 侵犯五十路电车痴汉在线 | 亚洲特级黄色视频 | 色爱综合| 日韩操B| 色94色 欧美 setu | 成年人视频中文字幕在线播放 |