V8十年故事:從農場誕生的星球最強JS引擎
這個月不僅是谷歌Chrome的十歲生日,也是V8的十週年紀念日。這篇文章講述了V8在過去10年中經歷的主要里程碑,以及在它誕生之前的那些祕密的歲月。
視訊:使用gource建立的V8程式碼庫視覺化演化程序,相當精彩(ofollow,noindex" target="_blank">https://youtu.be/G0vnrPTuxZA )。
V8誕生之前的祕密歲月
2006年秋天,谷歌聘請Lars Bak為Chrome瀏覽器構建一個新的JavaScript引擎,當時它還只是谷歌內部的一個祕密專案。後來,Lars從矽谷回到了丹麥的奧胡斯。Lars想留在丹麥,但那裡沒有谷歌辦事處,於是Lars和幾個最初參與該專案的工程師開始在他的農場辦公。新的JavaScript執行時被命名為“V8”,靈感源自50年代經典的“肌肉車”的引擎。後來,隨著V8團隊不斷成長,開發者從農場搬到了奧胡斯的一個現代化的辦公大樓裡。然後,整個團隊開始專注於構建地球上最快的JavaScript執行時。
V8專案的啟動和演化
2008年9月2日,V8與Chrome在同一天宣佈開源。最初的程式碼提交日期可追溯到2008年6月30日。在那之前,V8是在一個私有CVS儲存庫上開發的。最初,V8只支援ia32和ARM指令集,並使用SCons作為構建系統。
2009年,V8引入一個名為Irregexp的正則表示式引擎,改進了真實世界的正則表示式效能。隨著x64移植的引入,支援的指令集數量從兩個增加到三個。2009年,內嵌V8的Node.js釋出了第一個版本。在最初的Chrome漫畫中明確提到了將V8嵌入到非瀏覽器專案中的可能性,而Node.js做到了!Node.js成為最受歡迎的JavaScript生態系統之一。
2010年,V8引入了全新的優化JIT編譯器Crankshaft,從而極大提升了執行時效能。Crankshaft生成的機器程式碼比之前的V8編譯器(未命名的)快兩倍,而體積小了30%。同年,V8增加了第四個指令集:32位MIPS。
2011年,垃圾回收器效能得到了極大的改善。新的增量垃圾回收器大大減少了停頓時間,同時保持了極佳的峰值效能和低記憶體使用率。V8引入了隔離的概念,可以在一個程序中啟動多個V8執行時例項,為在Chrome中實現輕量級的Web Worker鋪平了道路。後來我們從SCons轉向GYP,這是第一次進行V8構建系統遷移。我們實現了對ES5 strict mode的支援。與此同時,開發工作從奧胡斯移交到了德國慕尼黑。
2012年是V8專案的基準測試年。團隊通過不斷的速度衝刺迭代來優化V8的效能,並使用Sunspider和Kraken基準套件來測量效能。後來,我們自己開發了一個名為Octane的基準測試套件(其核心是V8 Bench),它引領了峰值效能競賽,促進了所有主要JS引擎的執行時和JIT技術的大幅改進。所有這些努力導致的一個結果是從隨機抽樣轉向了一種基於確定性和計數的技術,用於檢測V8執行時分析器中的“熱”函式。
2013年,一個名為asm.js的JavaScript子集出現了。由於asm.js只支援靜態型別的算術運算、函式呼叫和基本型別的堆訪問,因此asm.js程式碼的執行效能是可預測的。我們釋出了新版Octane(Octane 2.0),對現有基準測試進行了更新,並針對asm.js等用例增加新的基準測試。Octane促進了編譯器優化的發展,例如分配摺疊和用於型別轉換和預定的基於分配站點的優化,大大提高了峰值效能。作為內部“Handlepocalypse”計劃的一部分,我們對V8 Handle API進行了徹底重寫,提升其易用性和安全性。同年,Chrome實現的TypedArrays從Blink轉到了V8中。
2014年,V8通過併發編譯將JIT編譯的一些工作從主執行緒中移除,以此來減少堵塞,並顯著提升了效能。後來,我們推出了名為TurboFan的新優化編譯器的初始版本。同時,我們的合作伙伴將V8移植到三個新的指令集架構上:PPC、MIPS64和ARM64。繼Chromium之後,V8轉向了另一個構建系統GN。V8的測試基礎設施得到了顯著改進,可以使用Tryserver在構建開始之前測試每個補丁。在原始碼控制方面,V8從SVN遷移到了Git。
2015年是V8最繁忙一年。我們實現了程式碼快取和指令碼流,大大加快了網頁載入速度。那年晚些時候,我們開始了新直譯器Ignition的開發工作。我們嘗試了strong mode,打算讓它成為JavaScript的子集,以實現更強和更可預測的效能保證。我們實現了strong mode,但後來發現它所帶來的好處並不足以抵消我們所花費的成本。而我們新增的提交佇列卻大大提高了生產力和穩定性。V8的垃圾回收器也開始與Blink等嵌入器合作,以便在空閒時進行垃圾回收。空閒時垃圾回收顯著減少了垃圾回收停頓和記憶體消耗。當年的12月分,第一個WebAssembly原型來到了V8上。
2016年,我們釋出了最後一組ES2015(以前稱為“ES6”)特性集(包括promise、類語法、詞法作用域、解構等),以及一些ES2016特性。我們還開始推出新的Ignition和TurboFan管道,用它來編譯和優化ES2015和ES2016特性,並將Ignition作為低端Android裝置的預設配置。我們在PLDI 2016大會上展示了我們的空閒時垃圾回收工作成功。我們啟動了Orinoco專案,這是一個針對V8的併發垃圾回收器,旨在減少主執行緒垃圾回收時間。我們將效能工作從合成微基準轉向了測量和優化實際效能。出於除錯的目的,V8檢查器從Chromium遷移到了V8,允許任何V8嵌入器(不僅僅是Chromium)使用Chrome DevTools來除錯在V8中執行的JavaScript。WebAssembly從原型轉為了實驗支援。V8獲得了ACM SIGPLAN程式語言軟體大獎。在這一年還新增了另一個移植平臺:S390。
2017年,我們終於完成了對V8引擎進行的重大修整,預設情況下啟用新的Ignition和TurboFan管道。這樣就有可能在後續從程式碼庫中移除Crankshaft(130,380個已刪除的程式碼行)和Full-codegen。我們推出了Orinoco v1.0,包括併發標記、併發掃描、並行清理和並行壓縮。我們正式將Node.js視為V8的一等嵌入器。從那以後,如果某些V8補丁無法通過Node.js測試套件的測試,就不能推出這些補丁。我們的基礎設施提供了正確模糊測試支援,確保任何程式碼都能產生一致的結果,無論是在怎樣的配置下執行。
在一個面向整個業界的協調啟動釋出中,V8預設啟用了WebAssembly。我們實現了對JavaScript模組以及ES2017和ES2018完整特性集的支援(包括非同步函式、共享記憶體、非同步迭代,rest/spread屬性和RegExp)。我們提供了對JavaScript程式碼覆蓋的原生支援,並啟動了Web Tooling Benchmark,用以幫助我們衡量V8的優化對實際開發者工具以及這些工具所生成的JavaScript輸出的效能的影響。我們可以藉助用於跟蹤JavaScript物件到C++ DOM物件的包裝器來解決Chrome中長期存在的記憶體洩漏問題,並有效地處理JavaScript和Blink堆上物件的閉包傳遞。我們後來使用這個基礎設施來提升開發者工具的堆快照能力。
2018年,一個行業範圍的安全事件(Spectre/Meltdown漏洞)顛覆了我們對CPU資訊保安的認知。V8工程師進行了大量有關安全攻擊問題的研究,以便更好地理解託管語言所面臨的威脅並提出解決措施。V8為執行不受信任程式碼的嵌入器提供了針對Specter和類似側通道攻擊的緩解措施。
最近,我們為WebAssembly釋出了一個名為Liftoff的基線編譯器,它大大減少了WebAssembly應用程式的啟動時間,同時提供了可預測的效能。我們釋出了BigInt,一個新的JavaScript原始型別,可以實現任意精度的整數。我們實現了嵌入式內建函式,並可以對它們進行惰性反序列化,從而顯著降低V8多個隔離的佔用空間。現在可以讓後臺執行緒編譯指令碼位元組碼。我們啟動了Unified V8-Blink Heap專案,可同步執行跨V8和Blink的垃圾回收。
效能的起伏
多年來,Chrome的V8 Bench分數顯示出V8的變化對效能產生了巨大影響。(我們正在使用V8 Bench,因為它是仍然可以在最初Chrome測試版中執行的少數基準測試之一。)
從2008年到2018年的Chrome V8 Bench分數
在過去十年中,我們在這個基準測試中的分數提升了4倍!
不過,你可能也注意到其中有兩次出現效能下降,它們分別對應於V8歷史上的兩個重大事件。2015年的效能下降發生在V8釋出ES2015特性基準版的時候,我們更多地關注特性的正確性,而忽略了初始版本的效能。我們以微小的效能回退換來能夠儘快讓開發者用上這些特性。在2018年初,因為Spectre漏洞,V8釋出了緩解措施以保護使用者免受潛在攻擊,導致效能再次下降。所幸的是,現在Chrome正在釋出了站點隔離,我們可以禁用這些緩解措施,從而恢復效能。
從圖中我們還可以看出,V8的效能從2013年左右開始趨於平穩。這是否意味著V8放棄並停止在效能方面繼續投入?恰恰相反!圖中所示的平穩說明了V8團隊從合成微基準(如V8 Bench和Octane)轉向了優化實際效能。V8 Bench是一箇舊的基準測試,它不使用任何現代JavaScript功能,與實際的生產程式碼也有一定的距離。下面是使用更新的Speedometer基準套件得出的結果:
從2013年到2018年Chrome的Speedometer 1得分
從2013年到2018年,V8 Bench顯示的改程序度微乎其微,但在同一時期,Speedometer 1得分上升了4倍。(我們使用了Speedometer 1,因為Speedometer 2使用了2013年還不支援的現代JavaScript特性。)
如今,我們有更好的基準測試,可以更準確地反映現代JavaScript應用程式。最重要的是,我們會主動測量和優化現有的Web應用程式。
總結
儘管V8最初是為谷歌Chrome而構建的,但它一直是一個獨立的專案,它有自己的程式碼庫,並提供了嵌入式API,讓其他程式可以使用它的JavaScript執行服務。在過去的十年中,這個專案的開放性不僅讓它成為Web平臺的關鍵技術,也成為其他環境(如Node.js)的關鍵技術。Web的故事可以說是源遠流長,慶祝Chrome和V8的10歲生日是一個重要的里程碑,但Web平臺的故事已經延續了25年。毫無疑問,Web的故事會繼續傳承下去。我們希望V8、JavaScript和WebAssembly在後續的Web故事中繼續扮演有趣的角色。同時,我們也很期待未來十年將會發生些什麼!
英文原文:https://v8project.blogspot.com/2018/09/10-years.html
感謝覃雲對本文的審校。