React Native發佈新一代JS引擎Hermes

前不久,Facebook在ChainReact 2019大會上正式推出了新一代JavaScript執行引擎Hermes。 Hermes 是一款小巧輕便的 JavaScript 引擎,專門針對在 Android 上運行 React Native 進行了優化。對於許多應用程序,只需啓用 Hermes 便可縮短啓動時間、減小內存使用量並縮小應用程序大小,此外由於它採用 JavaScript 標準實現,因此很容易在 React Native 應用中集成。 node

在這裏插入圖片描述

Hermes簡介

自ReactNative推出以來,有大量的APP接入並使用,其中也包括大型應用的主流程業務。隨着業務複雜度不斷上升,性能問題變得沒法忽視。react

在分析性能數據時,Facebook團隊發現 JavaScript 引擎是影響啓動性能和應用包體積的重要因素。因爲JavaScriptCore最初是爲桌面瀏覽器端設計,相較於桌面端,移動端能力有太多的限制,爲了能從底層對移動端進行性能優化,Facebook團隊選擇自建JavaScrip引擎,設計了Hermes,限於iOS AppStore審覈限制,目前僅用於Android平臺。android

Chain React大會上官方給出了Hermes引擎的一組測試數據:‘git

  • 從頁面啓動到用戶可操做的時間長短(Time To Interact:TTI),從4.3s減小到2.01s
  • App的下載大小,從41MB減小到22MB
  • 內存佔用,從185MB減小到136MB

能夠發現,切換到Hermes後,加載時長,App大小和內存佔有三個關鍵指標都有了顯著的提升。 github

在這裏插入圖片描述
因爲 Hermes 是針對移動應用優化的,所以咱們沒有計劃將其集成到任何瀏覽器或 Node.js 等服務端基礎架構中。在這些環境中現有的 JavaScript 引擎仍然是首選。

Hermes優化方案

在移動應用開發中,首次加載啓動,內存大小和應用大小都是衡量應用好壞的重要指標,所以Hermes也是從這些方面還對React Native應用進行優化。npm

字節碼預編譯

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

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

在這裏插入圖片描述
儘管壓縮後的字節碼比壓縮後的 JavaScript 源代碼略大,但因爲 Hermes 的原生代碼體積較小,所以 Hermes 從總體上減小了 React Native 項目Android 應用的體積。

無 JIT

爲了加快執行速度,流行的 JavaScript 引擎能夠將頻繁解釋的代碼編譯爲機器碼,這項工做由即時(JIT)編譯器執行。瀏覽器

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

垃圾回收策略

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

所以,爲了儘可能優化引擎使用的內存和 VA 空間,咱們構建了一個具備如下功能的垃圾回收器,主要的措施有:

  • 按需分配:僅在須要時以塊的形式分配 VA 空間。
  • 非連續:VA 空間沒必要在單個內存範圍內,這避免了 32 位設備上的資源限制。
  • 移動:可以移動對象意味着能夠對內存進行碎片整理,並將再也不須要的塊返回給操做系統。
  • 分代:每次 GC 時不掃描整個 JavaScript 堆,減小 GC 時間。

集成Hermes

快速上手Hermes

Faceback團隊已經將Hermes工具上傳到了npm : hermesvm。hemres工具能夠直接運行JS代碼、轉換字節碼而且提供很是多的參數進行調優控制。

例如,下面是hermesvm執行JS代碼和轉換bytecode功能,代碼以下:

// 建立hermes_test文件,內容:print("This is Hermes Demo");
vim hermes_test.js

// 直接執行純文本js
~/node_modules/hermesvm/osx-bin/hermes hermes_test.js
This is Hermes Demo

// 轉換成bytecode
~/node_modules/hermesvm/osx-bin/hermes --emit-binary hermes_test.js -out hermes_test.hbc

// 執行字節碼
~/node_modules/hermesvm/osx-bin/hermes hermes_test.hbc
This is Hermes Demo
複製代碼

在新工程中集成

目前 Hermes 是一個可選的 React Native 功能。若是要啓用Hermes,須要確保 React Native項目的版本在0.60.2 以上,而且還須要對android/app/build.gradle 文件並進行如下更改。

project.ext.react = [
  entryFile: "index.js",
  enableHermes: true
]
複製代碼

若是應用已經至少構建了一次,請使用以下命令進行清理。

cd android && ./gradlew clean
複製代碼

而後,就能夠正常開發和部署應用。

react-native run-android
複製代碼

在這裏插入圖片描述

調試

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

在這裏插入圖片描述

Hermes、JavaScriptCore和V8 的對比

通過官方的數據驗證,Faceback團隊提出的關鍵性指標相較於原先的JavaScriptCore方案都有了顯著提升。

首先,是原生 so文件的大小方面,RN所依賴的必要so庫,Hermes比JavaScriptCore減小了約16%(單armeabi架構壓縮後下降了0.5M左右),V8則要遠大於Hermes和JavaScriptCore。

在這裏插入圖片描述
接下來,就是內存的波動狀況,拿 RNTester工程測試進入RN頁面滑動進入若干頁面並退出以後,內存的波動狀況比較能夠看到,V8和Hermes內存增加要更加平滑。
在這裏插入圖片描述
接下來是CPU波動狀況,拿RNTester工程測試進入RN頁面滑動進入若干頁面並退出以後,對比CPU波動狀況。Hermes明顯好於V8和JavaScriptCore。,以下圖所示。
在這裏插入圖片描述

Hermes存在的問題

相比JavaScriptCore來講,Hermes的確有不少的優勢,但不是說必定好於JavaScriptCore,隨着測試和集成的進行,Hermes帶來的問題逐漸顯現。

bytecode文件佔用size過大

經過測試,Hermes編譯的字節碼文件比純文本js文件增大100%。所以,打出的RN包就會比較大,而且動態下發RN增量包時,因爲是二進制文件diff,差分效率也會下降。

爲了解決這個問題,咱們根據Hermes的特性,轉變思路,將Hermes的bytecode編譯放到客戶端去作,客戶端同時存儲js和bytecode文件,若是有bytecode編譯完成則使用Hermes,不然仍然使用JavaScriptCore。

Hermes開源項目提供了編譯bytecode的complieJS方法,但這部分代碼沒有默認打包到RN的Hermes引擎中,咱們稍加整合、封裝,經過JNI暴露出來,供業務使用。

執行純文本js耗時長

在客戶端將純文本js轉換成bytecode以前,咱們讓Hermes加載純文本。但實際測試下來,發現Hermes加載純文本的性能比JavaScriptCore要慢將近30%。主要緣由是Hermes刪除JIT功能,導致對純文本js代碼運行變慢。

持續優化

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

相關連接:

hermesengine.dev/

www.oschina.net/p/hermes-js…

www.infoq.cn/article/8JE…

相關文章
相關標籤/搜索