精讀《手寫 SQL 編譯器 - 效能優化之快取》
1 引言
重回 “手寫 SQL 編輯器” 系列。這次介紹如何利用快取優化編譯器執行效能。
可以利用 Frist 集 與 Match 節點快取 這兩種方式優化。
本文會用到一些圖做解釋,下面介紹圖形規則:
First 集優化,是指在初始化時,將整體文法的 First 集找到,因此在節點匹配時,如果 Token 不存在於 First 集中,可以快速跳過這個文法,在文法呼叫鏈很長,或者 “或” 的情況比較多時,可以少走一些彎路:
如圖所示,只要構建好了 First 集,不論這個節點的路徑有多長,都可以以最快速度判斷節點是否不匹配。如果節點匹配,則繼續深度遍歷方式訪問節點。
現在節點不匹配時效能已經最優,那下一步就是如何優化匹配時的效能,這時就用到 Match 節點快取。
Match 節點快取,指在執行時,快取節點到其第一個終結符的過程。與 First 集相反,First 集可以快速跳過,而 Match 節點快取可以快速找到終結符進行匹配,在非終結符很多時,效果比較好:
如圖所示,當匹配到節點時,如果已經構建好了快取,可以直接調到真正匹配 Token 的 Match 節點,從而節省了大量節點遍歷時間。
這裡需要注意的是,由於 Tree 節點存在分支可能性,因此快取也包含將 “沿途” Chances 推入 Chances 池的職責。
2 精讀
那麼如何構建 First 集與 Match 節點快取呢?通過兩張圖解釋。
構建 First 集
如圖所示,構建 First 集是個自下而上的過程,當訪問到 MatchNode 節點時,就可以收集作為父節點的 First 集了!父集判斷 First 集收集完畢的話,就會觸發它的父節點 First 集收集判斷,如此遞迴,最後完成 First 集收集的是最頂級節點。
構建 Match 節點快取
如圖所示,訪問節點時,如果沒有快取,則會將這個節點新增到 Match 快取查詢佇列,同時路途遇到 TreeNode,也會將下一個 Chance 新增到快取查詢佇列。直到遇到了第一個 MatchNode 節點,則這個節點是 “Match 快取查詢佇列” 所有節點的 Match 節點快取,此時這些節點的快取就可以生效了,指向這個 MatchNode,同時清空快取查詢佇列,等待下一次查詢。
3 總結
拿 select a, b, c, d from e
這個語句做測試:
node 節點訪問次數 | Frist 集優化 | First 集 + Match 節點快取優化 |
---|---|---|
784 | 669 | 652 |
從這個簡單 Demo 來看,提效了 16% 左右。不過考慮到文法結構會影響到提效,對於層級更深的文法、能啟用深層級文法的輸入可以達到更好的效率提升。
4 更多討論
如果你想參與討論,請點選這裡,每週都有新的主題,週末或週一釋出。前端精讀 - 幫你篩選靠譜的內容。