Databend 性能調優 - 基礎篇

在軟件基礎設施領域,人們開始越來越重視代碼的性能優化,在滿足其可實現功能完整的同時,若可逐步去優化代碼,則能在相同硬件條件下達到更好的工作效率,進一步提高業務生產效率。尤其是大數據領域,比如 OLAP 數據庫通常服務於海量數據即席查詢分析的場景,一些看似不起眼的底層調優卻能在數據量級上進行優化疊加,從而達到優化系統整體分析查詢的性能目的,可謂星星之火可以燎原。

在這裏主要向大家做一個 Databend 性能調優相關的分享,共會分爲三次向大家介紹,如下所示:

1、基礎篇:代碼調優的前置知識

2、實踐篇 1:Databend 源碼性能調優實踐

3、實踐篇 2:Databend 的 Group By 聚合查詢爲什麼跑的這麼快?

在後續 Databend 打磨的過程中,我們也將介紹更多關於性能優化相關的設計,比如 Databend 新的 pipeline workflow 模型設計,Databend 如何將向量化和編譯執行結合等。

1、存儲器層次結構

隨着科技的不斷髮展,大家一定對存儲器越來越不陌生了,下圖中所呈現的內容爲存儲器的金字塔模型。

圖中存儲器層次結構從上往下爲:寄存器、L1 Cache、L2 Cache、L3 Cache、內存、SSD/HDD 硬盤。每個層次結構都有着不同的容量、緩存時間。從上往下來看,存儲容量越大,但速度越慢,成本越低,往上來看則相反。通常編寫高性能代碼的原則在於將數據訪問靠近上層,讓數據離 CPU 靠近,減少數據訪問的開銷。

2、延遲

上圖瞭解了通用的存儲器,我們再來介紹下存儲器大概的訪問延遲。延遲也表示爲完成計算或者 I/O 請求所需的時間,爲了量化這個指標,也會通過各種計算來衡量這個性能,例如 IOPS、吞吐量等等。

下圖 [1] 爲大家列出幾個每個程序員都應該瞭解的延遲數字,這些數字代表了計算機各類操作的一個大概耗時:

Jeff dean 曾要求這些延遲所有程序員都應該熟記於心, 它讓我們在代碼調優的時候能有一個客觀的度量值。

3、局部性原理

基於現代計算機存儲器的設計,程序在執行的過程中存在着很強的局部性,局部性又分爲了「時間局部性」和「空間局部性」。

時間局部性:

時間局部性是指當一個數據在某一時間被訪問後,則有可能在不久後被再次訪問,所以我們看到很多程序會利用緩存來加速數據訪問。

空間局部性:

也稱數據局部性,得益於存儲器緩存的設計,它是指當一個數據在某一地址被訪問了之後,則訪問它相鄰位置的數據也會更加高效,常見於循環訪問數組數據中。

所以當我們利用好這些局部性原理,可以編寫更加 “局部性” 的代碼來提升 CPU 的性能和利用率。下圖中展示爲英特爾公司的 Core i7 Haswell 處理器的存儲器山模型,x 軸表示存儲器的容量大小,y 軸表示程序訪問的步長,z 軸表示存儲器訪問吞吐,其 3D 模型則爲圖中所示。

隨着存儲器的容量提升,性能基本呈倒退趨勢,但如果步長可以在一定短的區間內,可以非常好利用數據局部性的優化,比如 CPU 的 prefetch,從而達到提升吞吐的作用。

4、數據局部性示例

這裏再介紹一個矩陣的行優先和列優先遍歷訪問的例子,我們遍歷矩陣求和的方式是先循環行方式遍歷還是先循環列方式進行遍歷。如圖所示,在 Row-major order 中,行遍歷求和的性能遠高於列遍歷求和的性能,因爲行遍歷的方式數據能有更好的局部性,相鄰的數據可以在 CPU cache 中,數據可以以非常高效的方式訪問。同時,編譯器也能對局部性強的數據訪問進行優化,生成更加高性能的代碼,比如自動向量化。

5、性能表達

這裏引用 CASAPP [2] 中 Prefix-sum functions 的例子,在 psum1 函數中做的是直接使用一個 for 循環做加總求和,在 psum2 中,對循環進行展開,步長爲 2 進行遍歷。我們對兩個函數設置不同的循環次數,每次運行代碼都能得到一個 CPU 時鐘結果,將結果運用最小二乘法可以擬合成下面的兩條直線,直線的效率對應的就是函數性能的開銷。可以得出 psum2 的性能明顯優於 psum1。

6、實用工具和腳本介紹

在基礎篇的後面,介紹下 Databend 中常見的調優方式。

  1. Print & Log & Tracing metrics

(1) 最常見的還是通過日誌打印耗時,或者將耗時輸出到監控系統中

(2) prometheus, jaejer [3] 

  1. Perf is a good tool, pprof, flamegraph

(1) How to profile databend [4]

(2) Cargo flamegraph crate [5]

  1. cargo bench & criterion [6]

  2. databend-benchmark [7]

  3. ignore function && progress [8]

本文 ppt 地址:

[1]https://github.com/datafuselabs/datafuse-presentations/tree/master/meetup-20211203-performance-tuning-in-databend

[2]https://github.com/wubx/rust-in-databend/tree/main/24-performance-tuning-in-databend

7、文章相關引用

[1]https://colin-scott.github.io/personal_website/research/interactive_latency.html

[2]https://csapp.cs.cmu.edu/

[3]https://github.com/datafuselabs/databend/pull/3628

[4]https://databend.rs/dev/development/profiling

[5]https://github.com/flamegraph-rs/flamegraph

[6]https://github.com/bheisler/criterion.rs

[7]https://github.com/datafuselabs/databend/blob/main/query/src/bin/databend-benchmark.rs

[8]https://github.com/datafuselabs/databend/blob/9393f5cf7da5827132acd479185878726caf58bf/tests/perfs/perfs.yaml

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/WTCL6w9ur6DyIBUWt1TvQA