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