效能優化是個手藝活

技術小能手發表於2018-10-18

大資料的技術本質就是高效能,效能優化也是程式設計師們的永恆話題。

這裡說的效能優化,主要是指在程式設計師的努力下能達到某種效能提升效果的過程。只要簡單換臺機器就能加速的事情,業主方要麼早就做過了,要麼就是條件不允許這麼做。

這時候,優化方案的關鍵在於演算法,具體來講,就是要設計出低複雜度的計算方案。我們說過多次,軟體不可能提高硬體的效能,只有採用了低複雜度的演算法,也就是計算規模有了實質性的下降時,才可能在相同硬體環境下獲得更優的效能。

不幸的是,這類方案通常都是定製化的,換一個場景可能就沒有效果了。換句話說,這些方案是專門為某個計算任務設計的。

以分組運算舉例:如果我們事先知道GROUP BY的結果集很小(可以在記憶體中放下),那麼單次遍歷不需要生成臨時外存資料就可以完成運算,而且也容易採用平行計算。而如果結果集很大、記憶體無法裝下時,則需要生成臨時快取資料用於保持中間結果,而這時候也幾乎不可能再用並行機制,因為外存的併發衝突經常會抵銷掉並行帶來的好處。而如果我們知道用於分組的資料已經針對分組欄位有序時,則即使是大結果集也可以不必生成臨時快取資料,這樣又可以採取平行計算的手段來提高效能。反過來,如果我們在大結果集時使用了小結果集的演算法,那就會導致記憶體溢位;而在無序情況下使用了有序演算法,則會得出錯誤的計算結果。

也就是說,看起來非常類似的計算任務,在不同場景下會採用很不一樣的動作,這需要了解計算和資料特徵才能做出選擇。

如果要考慮通用的場景,那隻能使用最保守的演算法。對於分組運算來講,也就是使用大結果集且資料無序的演算法,這樣雖然能保證正確計算出結果,但效能就會很差。

這樣,效能優化就是個手藝活了!

手藝活需要就事論事地精雕細刻,這又需要我們手裡有個好工具,能夠讓我們快速實現設計出來的計算方案,不能發生想出好演算法卻無法實現的迥境。

作為當前資料計算主流的傳統關聯式資料庫在這方面做得很差。SQL提供的運算太少,而且透明度過高,程式設計師對計算過程的控制力度很弱,很難實現自己的想法。比如上面說過幾種在某些場景下可以提升效能的分組演算法,SQL都沒有直接在語法層面上提供,只能指望資料庫引擎的自動優化,而這又嚴重依賴於資料庫系統以及當前場景的複雜性,這就經常會發生我們明知有好演算法卻無能為力的現象。

事實上,SQL並不是完全沒有提供這種能力。有經驗的程式設計師在發現SQL執行過慢時都會去觀察資料庫採用的執行計劃,然後再去調整SQL語句讓資料庫採用自己期望的路徑去計算。有些資料庫也提供了一些關鍵字來人為改變執行計劃,從而達到控制計算過程的目標以期獲得更優效能。但無論如何,資料庫在這方面提供的功能仍然非常粗糙,遠遠滿足不了程式設計師進行效能優化的需求。

使用集算器的SPL程式設計能實現更高效能的原因就在於此。SPL中提供了大量僅適合某個場景的運算方法,程式設計師就可以根據運算和資料的特徵選擇合適的方法來組合出低複雜度的演算法。這時候,即使單項運算的效能並不比資料庫更高(用Java寫的集算器在基本運算方面一般不會比用C++寫的資料庫效能更好),但仍然經常能獲得數量級的效能提升,這就是演算法的力量。

總結一下,效能優化的關鍵在於兩條:1. 能根據任務特徵設計出更優的演算法;2. 有好的工具能迅速實現這個演算法。想不出好演算法時,有再好的工具也是白搭;而想出好辦法了,沒有好工具也只能乾瞪眼。

設計演算法雖然沒有固定的程式,但還是有一些常規套路,比如前面舉例說的分組運算的不同情況應當採用的手段。這需要充分了解運算原理,後續我會就這個話題寫一系列文章,介紹各種場景下可能採用的低複雜度演算法及其原理。初步列了一下提綱,這個系列話題估計能寫三五十篇文章出來。

原文釋出時間為:2018-10-16

本文作者:蔣步星

本文來自雲棲社群合作伙伴“資料蔣堂”,瞭解相關資訊可以關注“資料蔣堂”。


相關文章