Hugo 的效能優化(partialCached)
在源碼優化之前,最簡單的方式是先升級你的 Hugo 到 v0.164.0,這個版本以 hugo-yore v2.0.0 為例,280 頁的網站有 30% 的效能提升。
檢測
hugo --templateMetrics --templateMetricsHints
優化:通常都是頁面又 walk 一遍其他頁面導致 O(N^2) 複雜度,不然預設 O(N) 加上 Hugo 平行處理的機制沒什麼好優化了,頂多就是把檢測找到的複雜模板改用 partialCached 快取處理,然後資料結構基本觀念要有,不要大型數據還在用 range 跑或是用迴圈 merge 大型 dict。
雖然前面不斷抱怨 Hugo,不過 Hugo 真的很快,除了 partialCached 以外幾乎沒有地方需要你再煩惱,只需要有基礎的計算機組織概念,避免前面講的 range/merge 問題就好了。
Icon
不要小看 icon 渲染,頁面多、內容重複多的時候,這也會成為一個負擔。
基本上很多 icon 都會重複,比如複製按鈕、Github icon、link icon 這些會在多個頁面多個位置重複出現,甚至 header/footer 都會重複出現,這種重複出現的 icon 請無腦用 partialCached,常用的 icon 也就那 20~30 個,只要花 10MB 以內的記憶體即可輕鬆換來 10% 的效能提升。
templates.Defer
不要用,這不會加快速度,只會減慢,唯一的使用場景就是文檔裡面寫的那幾個渲染後處理:有些內容(例如需要動用大量資源、或依賴其他頁面尚未產生的資料的區塊)不適合在第一輪渲染頁面時就立刻算出來。
templates.Defer 的原理是
-
第一輪(正常渲染頁面時):當模板執行到 templates.Defer 包住的區塊,Hugo 並不會真的執行那段內容,而是先佔一個位置——生成識別碼,把「這段要延後執行的模板本體、當下的 context/data」記下來存進一個全域的待執行清單,然後在輸出的 HTML 裡先塞一段帶有識別碼的佔位標記字串。這個階段結束後,網站的第一輪 HTML 檔案已經全部寫到磁碟,但檔案裡到處散布著這些佔位標記,還不是最終內容。
-
第二輪(全部頁面渲染完之後才開始):Hugo 另外跑一個「渲染延遲內容」的階段,這時才會真的去執行剛剛記下來的那些模板本體、算出結果。關鍵是這一步是直接讀取已經寫到磁碟的 HTML 檔案,在檔案內容裡搜尋那些佔位標記,一找到就把標記替換成剛剛真正算出來的內容,再寫回去。
也就是說 Defer 每次呼叫都會讓磁碟 IO 變成兩次,磁碟 IO 正是所有效能優化裡面最高問題項目,因此 Defer 和加速渲染完全背道而馳。
以下是關鍵檔案的 permalink(commit 0796fa7a):
第一階段:templates.Defer / DoDefer
- https://github.com/gohugoio/hugo/blob/0796fa7aced4c27d0bad1d01f0aa2e99b943019f/tpl/templates/templates.go#L56-L71
- https://github.com/gohugoio/hugo/blob/0796fa7aced4c27d0bad1d01f0aa2e99b943019f/tpl/templates/templates.go#L134-L160
第二階段:renderDeferred / executeDeferredTemplates