查詢編譯與代碼生成

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

看下面的例子


查詢解釋1.png

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


查詢解釋2.png

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

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

Code Specialization

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

這樣做的好處是

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

Code Generation 代碼生成

兩種方法

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

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

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

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

Interpreted Plan 與 Templated Plan

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

比如對于
Select * from A where A.val=?+1


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

對查詢編譯性能的評估

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

JIT編譯(LLVM)

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


HIQUE的管道

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

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

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