摘要:它是對于內(nèi)部的一個重大改寫,以達(dá)到讓更快運(yùn)行的目的。擁有最高特異性的規(guī)則將會勝出。將來自于不同引擎的各種策略結(jié)合在一起,從而創(chuàng)造出一個超級快的新引擎。為了更平均的分配這些工作,使用了一個稱之為工作竊取的技術(shù)。
本文轉(zhuǎn)載自:眾成翻譯
譯者:Mactavish
鏈接:http://www.zcfy.cc/article/4041
原文:https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo
或許你聽說過 Quantum 項(xiàng)目。 它是對于 Firefox 內(nèi)部的一個重大改寫,以達(dá)到讓 Firefox 更快運(yùn)行的目的。我們將實(shí)驗(yàn)性的瀏覽器 Servo 的一部分功能調(diào)換出來,并對引擎的其他部分做除了重大的改進(jìn)。
這個項(xiàng)目好比一架正在飛行的飛機(jī)的引擎。我們對適當(dāng)?shù)牡胤竭M(jìn)行改進(jìn),一個一個組件地改進(jìn), 當(dāng)著這些組件準(zhǔn)備好的時候,你就能夠看到它對 Firefox 的影響。
第一個來自 Servo 的主要組件就是一個全新的CSS 引擎,名為 Quantum CSS (之前稱作 Stylo) — 現(xiàn)在在瀏覽器 Nightly 版本中已經(jīng)可以用于測試了。你可以進(jìn)入about:config 并設(shè)置 layout.css.servo.enabled 以確保這個功能可以被使用。
這個新引擎將四個瀏覽器中最先進(jìn)的革新技術(shù)結(jié)合在一起,創(chuàng)造出了這個超級 CSS 引擎。
它充分利用了現(xiàn)代的計(jì)算機(jī)硬件,使你的計(jì)算機(jī)的所有核心并行工作。這意味著它比原來快2倍,4倍甚至18倍。
另外, 它結(jié)合了現(xiàn)有的其他瀏覽器的最先進(jìn)的優(yōu)化方式。 所以即使它不是并行運(yùn)行,它依舊是一個非常迅捷的 CSS 引擎。
但是 CSS 引擎是做什么的呢?首先,讓我們看看 CSS 引擎是如何融入其他瀏覽器的。然后我們再來看 Quantum CSS 是如何做到更快的。
CSS 引擎的作用是什么?CSS 引擎是瀏覽器渲染引擎的一部分。渲染引擎將網(wǎng)站的 HMTL 和 CSS 文件渲染成屏幕上對應(yīng)的像素。
每個瀏覽器都有一個渲染引擎。在 Chrome 中它叫做 Blink,在 Edge 中它叫做 EdgeHTML, 在 Safari 中 它叫做 WebKit,在 Firefox 中它叫做 Gecko。
為了轉(zhuǎn)化這些文件成為像素點(diǎn),所有的這些渲染引擎都會做這些相同的事情:
解析這些文件成瀏覽器能夠理解的對象,包括 DOM。在這一點(diǎn)上, DOM 知道這個頁面的結(jié)構(gòu)。它知道元素之間的父子關(guān)系。但是它不知道這些元素該是什么樣子。
為了弄清楚這些元素究竟該長什么樣,對于每個 DOM 節(jié)點(diǎn),CSS 引擎會計(jì)算出要應(yīng)用哪些 CSS 規(guī)則,然后計(jì)算出那個 DOM 節(jié)點(diǎn)應(yīng)用的每個 CSS 屬性的值。
計(jì)算出每個節(jié)點(diǎn)的大小以及它在屏幕上的位置。 對要出現(xiàn)在屏幕上的東西創(chuàng)建它們所屬的盒子。盒子不僅僅代表 DOM 節(jié)點(diǎn),也會有在 DOM 節(jié)點(diǎn)內(nèi)部的盒子,比如文本行。
繪制這些不同的盒子,繪制可以發(fā)生在不同的層上。我覺得這個有點(diǎn)像過去用洋蔥皮紙上的手繪動畫。這使得瀏覽器可以只切換一個層而不用在其他層上重新繪制。
把這些不同的繪制的層,應(yīng)用任何像transform 這樣的合成屬性,然后把他們變成一張圖像。這基本上就像是給這些疊在一起的層拍一張照,這張圖像之后就會被渲染到屏幕上。
這意味著當(dāng)渲染引擎開始計(jì)算樣式,CSS 引擎有兩個東西:
DOM 樹
一張樣式規(guī)則的清單
它將遍歷每個 DOM 節(jié)點(diǎn),然后計(jì)算出對應(yīng) DOM 節(jié)點(diǎn)的樣式。對于這部分,它對當(dāng)前 DOM 節(jié)點(diǎn)的每個 CSS 屬性都給予一個值,哪怕樣式表沒有對這個屬性聲明一個值。
I think of it kind of like somebody going through and filling out a form. They need to fill out one of these forms for each DOM node. And for each form field, they need to have an answer.
我覺得這好某個人去填一張表單。他需要為每個 DOM 節(jié)點(diǎn)都填寫一張表單,然后表單的每個域都要填上最終的答案。
為了做到這一點(diǎn),CSS 引擎需要做兩件事:
計(jì)算出當(dāng)前節(jié)點(diǎn)需要應(yīng)用哪些規(guī)則?,又叫做 選擇器匹配
為任何空缺的值填補(bǔ)上父元素的值或者是默認(rèn)值,又叫做 層疊
選擇器匹配對于這一步, 我們將任何匹配當(dāng)前 DOM 節(jié)點(diǎn)的規(guī)則添加到一個列表,因?yàn)榭梢云ヅ涠鄠€規(guī)則,對于同個屬性也可能會有多次聲明。
另外,瀏覽器本身也會添加一些默認(rèn) CSS (稱作 user agent style sheets)。那么 CSS 引擎怎么知道要選擇哪個值呢?
這時候特異性規(guī)則就出場了。CSS 引擎基本上會創(chuàng)建一個試算表。然后它會基于不同列分出不同的聲明。
擁有最高特異性的規(guī)則將會勝出。所以根據(jù)這張表,CSS 引擎會應(yīng)用上它能應(yīng)用的值。
其他的, 我們會用到層疊。
層疊層疊讓 CSS 更易于書寫和維護(hù)。因?yàn)閷盈B,你可以在 body 上設(shè)置 color 屬性,然后你就知道 p元素和 span 元素以及 li 元素都使用那個顏色 (除非你有更多具體的樣式覆蓋)。
為了做到這點(diǎn),CSS 引擎會查看樣式表單中空的盒子。如果這個屬性默認(rèn)是繼承的,那么 CSS 引擎就會向樹上查找是否有一個祖先節(jié)點(diǎn)有值。如果沒有任何祖先節(jié)點(diǎn)有這個值,或者這個屬性沒有繼承,那么這個屬性就會得到一個默認(rèn)值。
所以現(xiàn)在這個 DOM 節(jié)點(diǎn)所有的樣式都已經(jīng)計(jì)算好了。
旁注: 樣式結(jié)構(gòu)共享剛剛那個展現(xiàn)給你們的表單其實(shí)是有一些曲解的。CSS 有上百個屬性。如果 CSS 引擎保持著每個 DOM 節(jié)點(diǎn)的每個屬性,那內(nèi)存早就不夠用了。
反而, 引擎實(shí)際上干的事情,叫做樣式結(jié)構(gòu)共享。他們將有關(guān)聯(lián)的數(shù)據(jù)(比如字體屬性)存到不同的對象上,叫做樣式結(jié)構(gòu)。然后,計(jì)算出的樣式只是通過指針指向具體的樣式對象,而不是把所有的屬性都放在相同的對象上。對于每種屬性,都有一個指針指向擁有對應(yīng) DOM 節(jié)點(diǎn)樣式的值的樣式結(jié)構(gòu)。
這樣既節(jié)省了內(nèi)存又節(jié)省了時間。 擁有相似屬性的節(jié)點(diǎn)(比如兄弟節(jié)點(diǎn))只是指向他們相同的結(jié)構(gòu)并共享那些屬性。同時又因?yàn)樵S多屬性都是繼承的,所以的祖先節(jié)點(diǎn)可以和任何不指定具有自己重寫屬性的后代節(jié)點(diǎn)共享同一個結(jié)構(gòu)。
現(xiàn)在,我們怎么樣讓它變得更快?這就是沒有優(yōu)化過的樣式計(jì)算看起來的樣子。
瀏覽器在樣式計(jì)算里做了很多事情。 這個過程并不只是發(fā)送在頁面第一次加載的時候。隨著用戶和頁面的不斷交互,這個過程在不斷地重復(fù),無論是將鼠標(biāo)懸停在元素之上還是改變 DOM 結(jié)構(gòu)都會觸發(fā)樣式的改變
這意味著 CSS 樣式計(jì)算是實(shí)現(xiàn)優(yōu)化的重要選項(xiàng)。在過去的20年內(nèi),瀏覽器一直在嘗試各種的優(yōu)化策略。Quantum CSS 將來自于不同引擎的各種策略結(jié)合在一起,從而創(chuàng)造出一個超級快的新引擎。
那么現(xiàn)在就讓我們來看一下他們是如何一起發(fā)揮作用的。
所有的運(yùn)行都是并行的Servo 項(xiàng)目 (也就是 Quantum CSS 的起源) 的內(nèi)容是使一個實(shí)驗(yàn)性的瀏覽器將頁面上所有不同部分都并行渲染。這意味著什么呢?
計(jì)算機(jī)就像人類的大腦。有一個專門用于思考的部分——算數(shù)邏輯單元(ALU)??拷@部分,有一些用于儲存短期記憶——寄存器(register)。他們共同組成了 CPU 。然后還有一些用于儲存長期記憶,也就是 RAM 。
早期使用這樣的 CPU 的電腦一次只能處理一件事情。但是經(jīng)過近十年的發(fā)展,CPU 已經(jīng)進(jìn)化成可以擁有由多個 ALU 和寄存器組合成的核心。這意味著 CPU 可以一次并行處理多件事情。
Quantum CSS 利用了當(dāng)今電腦最新的這些特性將不同 DOM 節(jié)點(diǎn)的樣式計(jì)算分配給不同的核心。
或許這看起來是一件非常簡單的事情,僅僅是將樹的分支分開在不同的核心上處理。因?yàn)槟承┰颍詫?shí)際上卻比想象中的要困難很多。其中之一就是 DOM 樹通常是不平衡的。這意味著可能某個核心的工作量要比其他的核心要多很多。
為了更平均的分配這些工作,Quantum CSS 使用了一個稱之為工作竊?。╳ork stealing)的技術(shù)。處理一個 DOM 節(jié)點(diǎn)時,代碼會獲取他的直接子元素,然后將他們分為一個或多個 “工作單元”。然后這些工作單元會被放進(jìn)一個隊(duì)列之中。
一旦其中一個核心完成了它當(dāng)前隊(duì)列中的任務(wù),那么他就會從其他的隊(duì)列中去尋找新的任務(wù)。這意味著我們不必提前遍歷整棵樹去計(jì)算他們的平均任務(wù)就可以均勻地分配任務(wù)。
在大多數(shù)的瀏覽器之中,很難保證這個方法的正確性。并行性是眾所周知的難題,而 CSS 引擎又十分復(fù)雜。 恰好它又處于渲染引擎中的另外兩個非常復(fù)雜的部分—— DOM 和布局之間。所以它很容易產(chǎn)生 bug,而且因?yàn)椴⑿行运a(chǎn)生的叫做數(shù)據(jù)競爭的 bug 難以追蹤。我會在 另一篇文章中闡述更多這類 bug。
如果你的程序接受了來自成百上千的工程師的辛勤奉獻(xiàn),如何讓你的程序不怕在并行環(huán)境從運(yùn)行呢?這就是 Rust 的意義所在。
有了 Rust, 你就可以靜態(tài)地驗(yàn)證以確保沒有數(shù)據(jù)競爭。這意味著通過提前防止難以調(diào)試的 bug 寫入你的代碼之中,你可以避免這些難以調(diào)試的 bug。而編譯器是不會讓你這么做的。將來我會撰寫更多關(guān)于這個內(nèi)容的文章。與此同時,你可以觀看這個視頻 intro video about parallelism in Rust 或者這個視頻 more in-depth talk about work stealing。
有了這個,CSS 樣式計(jì)算變成了一個所謂的尷尬的并行問題——很少有東西會阻止你在并行中更高效地運(yùn)行。這意味著我們可以得到接近線性的速度提升。假如在你的電腦上有四個核心,那么它會以接近原來四倍的速度運(yùn)行。
通過規(guī)則樹來加快樣式重置對于每個 DOM 節(jié)點(diǎn), 都需要CSS 引擎去遍歷所有的規(guī)則去實(shí)現(xiàn)選擇器匹配。對于大多數(shù)的節(jié)點(diǎn),這個匹配很大程度上不會經(jīng)常發(fā)生變化。比如,當(dāng)用戶把鼠標(biāo)懸停在一個父元素上,匹配的規(guī)則或許會發(fā)生變化。但是我們?nèi)匀恍枰獮樗械暮蟠刂匦掠?jì)算樣式來處理屬性繼承,然而匹配規(guī)則的后代元素很有可能不會發(fā)生任何變化。
如果我們可以為這些匹配到的后代元素這個記錄就好了,這樣我們就不用對他們再進(jìn)行選擇器匹配了。這就是所謂的規(guī)則樹——從 Firefox 的上一代 CSS 引擎 — does 中借來。
CSS 引擎會通過這個過程計(jì)算出需要匹配的選擇器,并通過特異性將他們分類出來。通過這個方式,就創(chuàng)建了鏈接的規(guī)則列表。
這個列表將會被添加到樹中。
CSS 引擎會嘗試保存最少分支的樹。為了做到這一點(diǎn),它會盡量嘗試復(fù)用分支。
如果在列表中的大多數(shù)選擇器和已有的分支相同,那么它會沿用同樣的路徑。但是它有可能會遇到這種情況——列表中的下一條規(guī)則并不在當(dāng)前樹的分支中,只有在這種情況下它才會添加一個新的分支。
DOM 節(jié)點(diǎn)會得到指向最后被插入的規(guī)則的指針(在這個例子當(dāng)中,就是 div#warning 規(guī)則)。這是最這是最特殊的地方。
關(guān)于樣式重置,引擎會做一次快速檢查,去檢查父元素上的改變是否會潛在地改變子元素上匹配的規(guī)則。 如果不是,那么對于任何的后代元素,引擎可以通過后代元素上的指針去獲取那條規(guī)則。從這里,它能夠順著樹回到根節(jié)點(diǎn)以獲取完整的規(guī)則匹配的列表,從最具體的到最不具體的。這意味著它能夠完全跳過選擇器匹配和排序。
這個可以大大減少在樣式重置期間的工作。但是在初始化樣式的時候仍然需要很多工作。如果你有10,000 個節(jié)點(diǎn),你仍然需要進(jìn)行 10,000 選擇器匹配。但是也有其他的方式去加速這個過程。
通過樣式緩存共享加速初始渲染 (以及層疊)試想一個擁有上千個節(jié)點(diǎn)的頁面,許多節(jié)點(diǎn)都會匹配同樣的規(guī)則。比如,一個很長的維基百科的頁面。 在主內(nèi)容區(qū)域的段落都最終會匹配相同的規(guī)則,擁有同樣的計(jì)算后的樣式。
如果不進(jìn)行優(yōu)化, CSS 引擎就不得不為每個多帶帶的段落進(jìn)行選擇器匹配和樣式計(jì)算。但是如果有一種方法能夠證明這個樣式在段落與段落之間都是相同的,那么引擎就可以只做一次運(yùn)算,并將每個段落節(jié)點(diǎn)都指向同樣的計(jì)算樣式。
這就是所謂的樣式緩存共享 —— 被 Safari 和 Chrome—does 所啟發(fā)。當(dāng)引擎處理完一個節(jié)點(diǎn)時,計(jì)算樣式會被放入緩存中。然后,在引擎開始計(jì)算下一個節(jié)點(diǎn)的樣式之前,它會運(yùn)行一些檢查,檢測是否有可用的緩存。
這些檢查是:
兩個節(jié)點(diǎn)是否擁有相同的 id, 類名, 或者其他?如果是,那么他們會匹配到相同的規(guī)則。
對于所有那些不是基于選擇器的——內(nèi)聯(lián)樣式,引擎會檢查比如,節(jié)點(diǎn)是否有相同的值?如果是,那么先前的規(guī)則要么不被覆蓋要么以同樣的方式被覆蓋。
節(jié)點(diǎn)的父元素是否指向相同的計(jì)算樣式對象?如果是,那么他們的繼承值將會相同。
從一開始,這些檢查就處于早期的樣式共享緩存中。但是可能仍然會有許多樣式不一定匹配的個例。比如,如果 CSS 規(guī)則使用了 :first-child 選擇器,那么兩個段落就不一定會匹配。即使這些檢查建議它們是匹配的。
在 WebKit 和 Blink 中,這些情況會放棄使用樣式共享緩存。隨著更多的站點(diǎn)使用這些現(xiàn)代選擇器,這種優(yōu)化策略變得越來越不中用了,所以最近 Blink 團(tuán)隊(duì)已經(jīng)移除了這個功能。結(jié)果卻發(fā)現(xiàn)有另外一種方式來使樣式共享緩存能夠跟上這些改變。
在 Quantum CSS 中,我們將這些怪異的選擇器都集中起來然后檢查它們是否在 DOM 節(jié)點(diǎn)中使用。然后我們將結(jié)果存為 1 和 0。如果兩個元素有相同的 1 和 0,那么我們就確定了它們是匹配的。
如果一個 DOM 節(jié)點(diǎn)能夠共享已經(jīng)計(jì)算好的樣式,那么你就可以跳過許多的任務(wù)。因?yàn)轫撁嫱ǔ6加泻芏鄻邮较嗤墓?jié)點(diǎn),樣式共享緩存便能夠節(jié)省內(nèi)存并真正地加快運(yùn)行速度。
結(jié)論這是的一個從 Servo tech 到 Firefox 的重大技術(shù)遷移。一路上,我們學(xué)到了如何將寫在 RUST 中的現(xiàn)代的高性能的代碼帶到 Firefox 的核心中。
我們非常高興能夠?qū)?Quantum 這個龐大的項(xiàng)目給用戶帶來第一時間的體驗(yàn)。我們很高興能讓你嘗試使用,如果你 發(fā)現(xiàn)了任何問題請告知我們。
關(guān)于Lin Clark
Lin 是 Mozilla Developer Relations 團(tuán)隊(duì)的一名工程師。 She 專注于 JavaScript, WebAssembly, Rust, 以及 Servo,同時也繪制一些關(guān)于編碼的漫畫。
code-cartoons.com
@linclark
More articles by Lin Clark…
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/84933.html
摘要:前端日報精選如何合理地設(shè)計(jì)的深入了解一個超快的引擎也稱全面了解作用域源碼分析二奇淫技巧總結(jié)整理下前端江湖面試對自己有益的題目。 2017-08-27 前端日報 精選 如何合理地設(shè)計(jì)Redux的State深入了解一個超快的 CSS 引擎: Quantum CSS (也稱?Stylo) ★ Mozilla Hacks全面了解JS作用域Zepto源碼分析(二)奇淫技巧總結(jié)整理下《前端江湖面試...
摘要:既然出現(xiàn)了,那怎么辦,目前并沒聽說出現(xiàn)什么新的技術(shù)替代它雖然它真的已經(jīng)很不適合現(xiàn)代的前端了,那么只能開發(fā)一個新的引擎提高性能,這就是火狐家的量子引擎又叫。這就是所謂的,火狐前一個引擎所做的那樣。 開始 本文翻譯自Inside a super fast CSS engine: Quantum CSS ,如果想要閱讀原文,可以點(diǎn)擊前往,以下內(nèi)容夾雜本人一些思考,翻譯也并不一定完全。 碎碎念...
摘要:既然出現(xiàn)了,那怎么辦,目前并沒聽說出現(xiàn)什么新的技術(shù)替代它雖然它真的已經(jīng)很不適合現(xiàn)代的前端了,那么只能開發(fā)一個新的引擎提高性能,這就是火狐家的量子引擎又叫。這就是所謂的,火狐前一個引擎所做的那樣。 開始 本文翻譯自Inside a super fast CSS engine: Quantum CSS ,如果想要閱讀原文,可以點(diǎn)擊前往,以下內(nèi)容夾雜本人一些思考,翻譯也并不一定完全。 碎碎念...
摘要:既然出現(xiàn)了,那怎么辦,目前并沒聽說出現(xiàn)什么新的技術(shù)替代它雖然它真的已經(jīng)很不適合現(xiàn)代的前端了,那么只能開發(fā)一個新的引擎提高性能,這就是火狐家的量子引擎又叫。這就是所謂的,火狐前一個引擎所做的那樣。 開始 本文翻譯自Inside a super fast CSS engine: Quantum CSS ,如果想要閱讀原文,可以點(diǎn)擊前往,以下內(nèi)容夾雜本人一些思考,翻譯也并不一定完全。 碎碎念...
摘要:的主要組件包含了一個全新的引擎,稱為量子,也稱為。這個新引擎集成了四種不同瀏覽器的最新創(chuàng)新技術(shù),創(chuàng)造出一個全新的超級引擎。這可以發(fā)生在多個圖層上。最終,擁有最高特異性的規(guī)則會勝出。 原文:Inside a Super Fast CSS Engine: Quantum CSS(Aka Stylo), Lin Clark 注:原文發(fā)布于 2017 年 8 月,本文翻譯于 2018 年 4 ...
閱讀 2677·2021-09-28 09:35
閱讀 3316·2021-09-03 10:28
閱讀 2972·2019-08-30 15:43
閱讀 1530·2019-08-30 14:04
閱讀 1886·2019-08-29 17:02
閱讀 1880·2019-08-26 13:59
閱讀 783·2019-08-26 11:51
閱讀 3346·2019-08-23 17:16