本文翻譯自Inside a super fast CSS engine: Quantum CSS ,若是想要閱讀原文,能夠點擊前往,如下內容夾雜本人一些思考,翻譯也並不必定徹底。css
爲何翻譯這篇文章尼,一開始只是好奇,基本在前端技術圈子混過都知道火狐正在用Rust語言開發新的瀏覽器引擎,做爲前端開發對火狐的感情仍是大大的有(雖然如今已經離不開chrome了),可是仍是但願火狐可以再次引領Web的變革。
能夠說前端這幾年解決了前端工程化的不少痛點,可是性能這個坎依舊,指望webassembly儘快普及,可是對於前端一定又是一場腥風血雨,前端不會一直是如今這樣的前端。既然webassembly出現了,那css怎麼辦,目前並沒據說出現什麼新的技術替代它(雖然它真的已經很不適合現代的前端了),那麼只能開發一個新的引擎提升性能,這就是火狐家的量子引擎:Quantum CSS(又叫Stylo)。前端
這是火狐正在開發的Quantum項目,目的固然是爲了讓瀏覽器更快,從上圖能夠看獲得各個模塊,而Quantum CSS處於中間位置,這跟它在整個渲染過程當中的位置同樣,利用Rust能夠至關有效利用現代處理器多核心的特性,可以幾倍的提速。既然這麼厲害,那從哪裏能夠體驗尼:在火狐Nightly版進入about:config設置layout.css.servo.enabled 屬性爲 true就能夠體驗這吊炸天的引擎。
web
站在巨人的肩膀上,固然除了利用現代處理器的並行能力,還借鑑當前各家瀏覽器積累的一些優化技術,接下來會一一解析這些優化技術,如何讓引擎更快。chrome
CSS引擎是瀏覽器渲染引擎的一部分,而渲染引擎會把咱們的HTML和CSS轉換成屏幕上的像素(也就是畫面吧)。
各家瀏覽器都會有自家的渲染引擎,例如谷歌的Blink,Edge的EdgeHTML,Safari的Webkit和火狐的Gecko,雖然有這麼多引擎,可是他們都作着一樣的事情:把HTML和CSS渲染成咱們能夠感知的界面。
而他們內部的工做須要:前端工程化
綜合後,咱們能夠知道CSS引擎開始計算樣式時須要兩樣東西:DOM的節點樹和一系列的樣式規則。
CSS引擎會遍歷全部DOM節點並計算每一個節點所應用的樣式,它會讓DOM節點每一個CSS屬性都有一個值,就算你在樣式表中並無聲明,它可能來自繼承或者默認值,或者客戶端的樣式表(User Agent Style)。
能夠認爲引擎就像填表格同樣,把這些最後計算出來的值一個一個填進去。
瀏覽器
爲了獲得上面的表格,CSS引擎須要作兩件事:ide
首先找出配置當前的節點的樣式規則,放到一個list上去,這裏也包括客戶端的樣式表。
佈局
而後會計算各個樣式規則之間的權重,而且根據權重排序。
性能
根據權重大小,得出最終應用的樣式屬性的值。
學習
級聯(The cascade)目的是爲了讓CSS更容易編寫和維護,因爲級聯的存在,你能夠在body上設置color屬性,而li,p,span等元素能夠直接使用一樣的color,不須要每一個元素都要去定義一次。
爲了實現這個功能,CSS引擎會從表格裏面尋找一些屬性值仍然爲空的值,若是屬性默認是繼承的話,CSS引擎會從父節點那裏繼承屬性值,若是全部父節點都沒有定義該屬性值的話,就會使用默認的值。
如今咱們的表格都填滿了
上述表格的形式,只是一種表現方式,引擎真實的內部不是這樣的。CSS擁有成千上百的屬性,若是引擎爲每一個節點都生成這樣一張表,會很快耗掉全部內存。
相反引擎內部一般會使用style struct sharing,樣式的數據會集中在不一樣對象裏面(style struct),而後使用指針指向這些對象。
這會很大程度上節省內存,由於各個節點間都頗有可能擁有類似的屬性值(例如兄弟節點間),另外由於不少屬性也是經過繼承獲取的,因此父節點能夠跟子節點間共享這些屬性值。
若是咱們不去優化這些工做,整個樣式計算工做就會是這樣:
這是巨量的工做,並且並不只僅在頁面加載的時候發生,它會隨着用戶的交互時刻都在存在(例如hover一個元素,CSS引擎須要重新計算樣式)。
這樣就意味着必須得去優化樣式的計算工做,在過去20年,已經測試過不一樣的優化策略,而Quantum CSS則是組合利用這些最優的優化策略。
咱們如今的CPU大多擁有多個核心,而Quantum CSS則會把不一樣DOM節點的樣式計算工做分配到不一樣的核心上去,可是實現也有至關的難度,其中一個緣由就是DOM節點樹並不必定均勻的,這會致使其中一部分核心工做負荷比其餘核心大。
爲了讓各個核心工做負荷更加合理,Quantum CSS使用了一種技術稱做 work stealing,當一個DOM節點被處理的時候,引擎能夠把它的子節點計算工做分紅幾個「work units」而且放進隊列中。
當其中一個核心清空自身隊列的工做後,它可以尋找其餘隊列上的其餘work units而後執行,這意味着咱們不須要提早就去分配好工做,在運行時也會達到最高的工做效率。
在大部分瀏覽器裏面,很難讓這種機制毫無錯誤的運行,並且CSS引擎自己就很是複雜,它在渲染引擎中兩個最複雜的模塊(DOM和layout)中間。這個過程很是容易產生bug,並且並行程序致使的bug很是難debug,能夠經過這篇文章瞭解更多。
對於每一個DOM節點,CSS引擎須要遍歷全部樣式規則去進行selector matching,且對於大部分節點這種matching並不會常常改變。例如,用戶hover一個父節點,它的樣式規則可能會改變,可是咱們仍然須要從新子節點的樣式規則去處理屬繼承的屬性值,而子節點以前匹配的規則頗有可能不會改變。
若是咱們記錄好子節點匹配哪些樣式規則,而不用每次都進行一次selector matching,這可能會獲得很大的優化。這就是所謂的rule tree,火狐前一個引擎所作的那樣。
CSS引擎會遍歷樣式規則幫DOM節點找出匹配的選擇器,而後根據權重排序,從而建立出一個樣式規則的鏈表,而後將這個鏈表添加到rule tree中。
CSS引擎會盡量利用已有的分支,爲rule tree保持最少的分支數。
若是大部分鏈表中大部分的選擇器,跟已存在的分支同樣,引擎會順着路徑,除非它到達一個節點,rule tree並不存在同樣分支,引擎就會添加一個新的分支。
在從新計算樣式的過程當中,引擎會快速檢查父節點的改變是否會致使子節點匹配的樣式規則改變。若是沒有,子節點能夠根據本身指向rule tree節點的指針計算樣式。引擎會rule tree的節點往上查找,獲取整個匹配的規則,從權重最大到權重最小的。這樣就能夠很輕鬆的跳過selector matching這一步了。
可是這樣仍然還有不少工做要作,畢竟一個頁面上節點成千上萬,這時候並行計算的魔法又能夠大顯神威了。
因爲整個頁面的節點可能會有成千上萬個,它們當中不少都匹配着相同的規則。例如wiki頁面中每一個p元素其實逗匹配着相同的樣式規則,擁有同樣的computed styles。
若是這裏沒有作優化,可能每一個段落都要從新計算,可是若是有一種方法來證實每一個段落的樣式規則都是同樣,引擎就只需計算一次就能夠了。
這就是所謂的style sharing cache,由Chrome和Safari所發明的一種優化方式,在引擎處理一個節點後會把computed style放到cache裏面,而後開始計算下一個節點的時候,引擎會先檢查cache裏面是否已經存在計算後的值。
而這些檢查包括:
可是也有不少其餘的狀況,致使這些檢查失效,例如:若是一個CSS規則使用了:first-child選擇器,就算兩個節點都已經符合上述的規則,結果也會是檢查不經過。
在Webkit和Blink, style sharing cache在這些狀況下會放棄檢查並不會使用cache。因爲大部分網站都使用了這些modern selectors(CSS3),這個優化的做用變得愈來愈少,因此Blink團隊最近把它移除了。
在Quantum CSS,咱們收集了全部這些怪異的選擇器(CSS3)而後讓它們加入檢查。咱們會把結果存儲爲0和1,若是兩個元素擁有一樣的0和1,那咱們就知道他們是匹配一樣的樣式規則。
這樣咱們就能夠繼續享受style sharing cache帶來的優化。
前半部分可讓咱們知道CSS引擎的工做內容,後半部分讓咱們瞭解新引擎是如何優化性能的,真的學習了不少,我想你也同樣。