查詢編譯與代碼生成

在內(nèi)存數(shù)據(jù)庫中提高吞吐量的唯一方式就是減少執(zhí)行指令數(shù)目。
大多代碼都是為了人能理解而不是單為了性能。
接下來從邏輯查詢方面進(jìn)行分析。

看下面的例子


查詢解釋1.png

根據(jù)這個(gè)例子可以得到一個(gè)查詢樹和查詢計(jì)劃。


查詢解釋2.png

這里的查詢語句被優(yōu)化器進(jìn)行重寫,重寫成Join。查詢樹和查詢計(jì)劃很好理解,但是對(duì)于CPU而言很不友好,過多的結(jié)構(gòu)和分支,無論for還是if都會(huì)產(chǎn)生大量的分支,導(dǎo)致CPU要不斷刷新管道和緩存;大量的函數(shù)調(diào)用導(dǎo)致CPU在內(nèi)存總不斷跳躍。
謂詞解釋.png

在執(zhí)行B.val = ? +1時(shí),有參數(shù)輸出,執(zhí)行這一條語句需要傳入?yún)?shù)、當(dāng)前元組、當(dāng)前元組所在表的格式,需要大量的函數(shù)調(diào)用,代價(jià)很大。

Code Specialization

一個(gè) 解決方式就是code specialization,產(chǎn)生專門針對(duì)DBMS task的代碼來較少指令數(shù)量。
對(duì)于上面的操作而言,都是一些通用的查詢編譯過程,支持大多數(shù)查詢。但是如果是不同的輸入具有相似的執(zhí)行模式,可以在本地編譯任何數(shù)據(jù)庫的CPU密集型實(shí)體,即對(duì)一些特殊的查詢進(jìn)行特定的編譯設(shè)計(jì)。
訪問方法
存儲(chǔ)過程
操作員執(zhí)行
謂詞評(píng)估
記錄操作

這樣做的好處是

  1. 屬性類型是先驗(yàn)已知的。
    數(shù)據(jù)訪問函數(shù)調(diào)用可以轉(zhuǎn)換為內(nèi)聯(lián)指針轉(zhuǎn)換。
    即,可以直接通過offset訪問元組中的某個(gè)屬性。
  2. 謂詞是先驗(yàn)已知的。
    可以使用原始數(shù)據(jù)直接比較來評(píng)估它們。
  3. 循環(huán)中沒有函數(shù)調(diào)用
    允許編譯器高效地將數(shù)據(jù)分發(fā)到寄存器并增加緩存重用。
    因?yàn)闆]有函數(shù)調(diào)用,所以只需要直接比較內(nèi)存塊的幾個(gè)offset的某個(gè)size的數(shù)據(jù)的關(guān)系即可。

Code Generation 代碼生成

兩種方法

  1. 移植
    把關(guān)系查詢計(jì)劃轉(zhuǎn)化為命令語言源碼(C/C++),然后再用傳統(tǒng)編譯器來產(chǎn)生本地碼

  2. JIT編譯(LLVM)
    生成可快速編譯為本地碼的查詢的中間表示(IR)

HIQUE - Code Generation
對(duì)于一個(gè)給定的查詢計(jì)劃,產(chǎn)生一個(gè)C/C++程序來實(shí)現(xiàn)查詢的執(zhí)行(將所有的謂詞和類型轉(zhuǎn)換都固定下來)。用線程的編譯器把代碼轉(zhuǎn)化為一個(gè)共享的對(duì)象,將起鏈接到DBMS的進(jìn)程中(類似于C++寫的python庫),然后調(diào)用exec函數(shù)(在一個(gè)進(jìn)程中啟動(dòng)另一個(gè)程序執(zhí)行的方法)
對(duì)于查詢計(jì)劃的特定部分,通過算法將這部分進(jìn)行重新編譯,并連接到最終的查詢程序中。

生成的查詢代碼的組件可以調(diào)用DBMS中任何其他函數(shù),這允許它使用與Interpreted Plan相同的組件:并發(fā)控制、檢查點(diǎn)、索引等。

Interpreted Plan 與 Templated Plan

  1. 需要明確表中的信息,如,屬性值大小等。
  2. 計(jì)算每個(gè)tuple的大小
  3. 返回tuple指針

比如對(duì)于
Select * from A where A.val=?+1


在解釋的計(jì)劃中, 其中g(shù)et_tuple首先要獲取從目錄中獲取表的格式,根據(jù)元組大小計(jì)算偏移量,然后返回元組的指針;eval()是遍歷謂詞樹獲取值,再獲取目標(biāo)屬性的偏移量,根據(jù)需要進(jìn)行比較操作,返回true/false。
而對(duì)于模板化的計(jì)劃,已經(jīng)定義好了像前面解釋的計(jì)劃中的獲取表的格式、元組大小這些信息,按照模板執(zhí)行就行了。

對(duì)查詢編譯性能的評(píng)估

  • Generic Iterators:通用模型
  • Optimized Iterators:對(duì)屬性值有特定代碼生成的模型,即,固定屬性值,固定屬性值大小
  • Generic Hardcoded:對(duì)謂詞和泛型迭代器產(chǎn)生特定的代碼
  • Optimized Hardcoded:直接訪問元組的評(píng)估模型
  • HIQUE:對(duì)特定查詢計(jì)劃產(chǎn)生代碼的評(píng)估模型

JIT編譯(LLVM)

關(guān)系型操作是對(duì)查詢有效的方式,但不是執(zhí)行查詢最有效率的方式。需要長時(shí)間進(jìn)行編譯C/C++ 源代碼 成 可執(zhí)行代碼;HIQUE不支持完全的管道,它的管道會(huì)因?yàn)槠渌麛?shù)據(jù)沒有處理完而等待其他數(shù)據(jù)。


HIQUE的管道

Hyper數(shù)據(jù)庫實(shí)現(xiàn)了JIT,用LLVM編譯器去編譯上面的查詢計(jì)劃,盡可能的將元組留存在CPU的寄存器中。核心部件是低級(jí)編程語言IR,不是所有的DBMS都需要用IR實(shí)現(xiàn),LLVM可以調(diào)用C++。
它會(huì)根據(jù)管道瓶頸生成代碼,但是多個(gè)core或者多個(gè)thread都不能同時(shí)在一個(gè)查詢中的多個(gè)管道上運(yùn)行,只能一個(gè)線程按順序在多個(gè)管道上完成處理。但是可以安排在管道執(zhí)行的先后順序。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容