Facebook發佈全新JS引擎!專注提高React Native應用的性能

facebook 於昨日發佈了新的JavaScript引擎:Hermes,專注於提高React Native應用的性能,並且在市面上那些內存較少、存儲速度較慢且計算能力低下的移動設備上都有良好的表現。

移動應用變得愈加繁重的同時也愈加複雜了。開發者爲應用添加新功能時通常會遇到卡頓等性能問題。雖然出現性能問題的原因是多種多樣的,但用戶不關心這些,他們只希望自己在任何設備上使用應用時都能有流暢的體驗。

爲了提高Facebook應用的性能,我們的團隊不斷改進自己的JavaScript代碼和平臺。在分析性能數據時,我們發現JavaScript引擎本身是影響啓動性能和應用包體積的重要因素。有了這些數據,我們意識到必須在比PC端限制更多的移動環境中優化JavaScript性能。嘗試了各種方案後,我們構建了一個新的JavaScript引擎:Hermes。它旨在提高應用性能,專注於React Native應用,並且在市面上那些內存較少、存儲速度較慢且計算能力低下的移動設備上都有良好的表現。

在Chain React 2019大會上,我們發佈了Hermes JavaScript引擎。我們已經開源了Hermes引擎及用於React Native的Hermes集成。我們很高興能與開源社區合作,讓開發者立即開始使用Hermes。

Hermes如何提升React Native性能

對於基於JavaScript的移動應用而言,用戶體驗主要取決於下面這些指標:

  • 應用程序可用的時間,稱爲交互時間(TTI)

  • 需要下載的數據大小(在Android上就是APK大小)

  • 內存利用率

在Google Pixel上運行React Native應用的MatterMost性能指標,可反映印度等市場中流行智能手機的表現。

值得注意的是,這幾大指標與引擎執行JavaScript代碼時的CPU使用率關係並不大。關注這些指標意味着我們需要發展出與當今大多數JavaScript引擎不一樣的策略和取捨。因此我們的團隊需要從零開始設計和構建Hermes。專注於這些指標後,我們的實現爲React Native應用程序帶來了實質性的改進。

由於Hermes是針對移動應用優化的,因此我們沒有計劃將其集成到任何瀏覽器或Node.js等服務端基礎架構中。在這些環境中現有的JavaScript引擎仍然是首選。

Hermes的架構設計要素

移動設備存在諸多限制,諸如內存較少,閃存較慢等。於是我們在架構層面做了一些針對性優化設計,具體內容有:

字節碼預編譯

通常來說,JavaScript引擎會在加載後才解析JavaScript源代碼並生成字節碼,JavaScript代碼需要在生成字節碼後纔開始執行。爲了跳過這一步,Hermes引入了一個預編譯器,在移動應用構建過程中運行。這樣一來優化字節碼的時間可以更長,使字節碼更小、效率更高。現在還可以針對整個程序做優化,例如刪除重複數據和打包字符串表等。

字節碼的設計使其在運行時可以映射到內存中並解釋,而無需急切地讀取整個文件。許多中低端移動設備上性能較差的閃存I/O顯著增加了延遲,因此按需從閃存加載體積經過優化的字節碼會顯著提升TTI。此外,由於內存以只讀方式映射並由文件支持,因此不使用虛擬內存的移動操作系統(如Android)可以在內存不足時清除這些頁面,進而減少了內存較少的設備上殺掉進程的現象。

儘管壓縮後的字節碼比壓縮後的JavaScript源代碼略大,但由於Hermes的原生代碼體積較小,因此Hermes從整體上減少了Android React Native應用的體積。

無JIT

爲了加快執行,流行的JavaScript引擎可以懶惰地將頻繁解釋的代碼編譯爲機器碼。這項工作由即時(JIT)編譯器執行。

Hermes現在並沒有JIT編譯器。這意味着Hermes在某些基準測試中表現不會很出色,特別是那些依賴於CPU性能的基準測試。這一設計是有意爲之:這些基準很難反映移動應用程序的實際工作負載。我們也對JIT做過一些實驗,但我們認爲想要獲得真正的速度提升還是要關注上述現實指標。因爲JIT必須在應用程序啓動時預熱,所以它們難以改善TTI,甚至可能會損害TTI。此外,JIT會增加原生代碼體積和內存消耗,這會對我們的主要指標產生負面影響。JIT可能會拖累我們最關心的指標,因此我們選擇不實現JIT。以此爲代價,我們更關注Hermes的解釋器性能表現。

垃圾回收策略

在移動設備上內存的高效利用尤爲重要。低端設備的內存有限,通常也沒有操作系統虛擬內存,操作系統會強制殺掉使用過多內存的應用程序。當應用被殺後再次使用時需要緩慢地重啓,且後臺功能也會受到影響。在早期測試中我們瞭解到,在32位設備上運行大型應用時虛擬地址(VA)空間,尤其是連續的VA空間都能是一種有限的資源,就算用了物理頁面懶惰分配都沒多大幫助。

爲了儘量優化引擎使用的內存和VA空間,我們構建了一個具有以下功能的垃圾回收器:

  • 按需分配:僅在需要時以塊的形式分配VA空間。

  • 非連續:VA空間不必在單個內存範圍內,這避免了32位設備上的資源限制。

  • 移動:能夠移動對象意味着可以對內存進行碎片整理,並將不再需要的塊返回給操作系統。

  • 分代:每次GC時不掃描整個JavaScript堆,減少GC時間。

開發者體驗

開發者要開始使用Hermes時需要對其build.gradle文件做一些更改,並重新編譯應用程序。請參閱在React Native上遷移到Hermes的完整說明。

project.ext.react = [
  entryFile: "index.js",
  enableHermes: true
]

懶編譯

高迭代速度是基於JavaScript的平臺的主要優勢之一,但預編譯字節碼會削弱這一優勢。爲了快速重載,Hermes調試版本不使用預編譯;相反,它們在設備上懶惰地生成字節碼。這樣開發者就可以使用Metro或其他純JavaScript代碼源進行快速迭代。代價是懶惰編譯的字節碼不包括生產構建的所有優化特性。實際上雖然我們能注意到性能上的差距,但我們發現這種方法足以在不影響生產指標的情況下提供良好的開發者體驗。

標準兼容

Hermes目前的目標是ES6規範,我們打算在發展中跟緊最新的JavaScript規範。爲了優化引擎的大小,我們選擇不支持React Native應用程序中似乎不常用到的一些語言功能,例如代理和本地eval()。完整列表可以在我們的GitHub上查閱。

調試

爲了提供出色的調試體驗,我們通過DevTools協議實現了對Chrome遠程調試的支持。時至今日,React Native還只支持在Chrome中運行應用的JavaScript代碼時使用應用內代理調試。有了這種支持就能調試應用了,但React Native橋接器中不能同步原生調用。Hermes對遠程調試協議的支持允許開發者連接到在其設備上運行的Hermes引擎,並使用與生產中相同的引擎原生調試其應用程序。除了調試之外,我們還在考慮實現對Chrome DevTools協議的額外支持。

針對React Native的改進

爲了簡化Hermes的遷移工作並繼續在iOS上支持JavaScriptCore,我們構建了JSI;這是一種用於在C++應用程序中嵌入JavaScript引擎的輕量級API。此API使React Native工程師可以實現自己的基礎架構改進。Fabric就使用了JSI,它可以搶佔React Native呈現;TurboModules也用了JSI,它縮小了原生模塊的體積,可以根據React Native應用程序的需要懶加載。

React Native是我們最初的用例,也是我們到目前爲止大多數工作的重心,但我們並沒有就此止步。我們打算構建時間和內存分析工具,以幫助開發者更輕鬆地改進他們的應用程序。我們希望完全支持Visual Studio Code調試器協議,引入完成量等新功能。我們還希望發展其他移動用例。

沒有社區的參與,任何開源項目都不可能成功。我們希望大家能在自己的React Native應用程序中嘗試Hermes,看看它是如何工作的,並幫助我們讓Hermes更加大衆化。我們特別想知道社區認爲哪些用例最好用,用例是不是React Native都無所謂。

我們要感謝Tzvetan Mikov、Will Holen以及Hermes團隊的其他成員,他們爲Hermes的構建和開源工作做出了重要貢獻。

英文原文: https://code.fb.com/android/hermes/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章