RN 技術探索:Hermes Engine 初探

自從 Google 的 Flutter 發佈之後,Facebook 對 React-Native 的迭代開始快了起來,優化 React-Native 的性能表現,避免被 Flutter 比下去。最近一個比較大的動作是開源了一個 JavaScript 引擎,並將其包含到 React-Native 中。那麼這款引擎它有什麼不同,相比 V8、JSC 這些 JavaScript 引擎又有什麼優勢呢,現在本文來爲你揭曉。

1.
Hermes 引擎是什麼,優勢有哪些?

重要的事情提前說:Hermes 引擎是 Facebook 研發,在 React-Native Android 端用於替換 JavaScript Core 的 JavaScript 引擎。Hermes 引擎的優勢是適合移動端的輕量級 JavaScript 引擎,使用 aot 編譯,可以減少 Android 端內存使用,減小安裝包大小,提升執行效率。

2.
什麼是 JavaScript 引擎?

JavaScript 引擎是一個專門處理 JavaScript 腳本的虛擬機,一般會附帶在網頁瀏覽器之中。

3.
主流 JavaScript 引擎

V8(Google)、JavaScriptCore(Apple)、SpiderMonkey(Firefox)

4.
RN 中的 JavaScript 引擎

Weex,Android:V8,iOS:JavaScriptCore

RN,Android:JavaScriptCore(Hermes、V8),iOS:JavaScriptCore(Apple 要求)

注:Hermes Engine在React-native 0.60.2 版本後支持

5.
Hermes 的特色

預編譯字節碼(引擎加載二進制代碼效率高於運行JS腳本)

無 JIT 編譯器(減小了引擎大小,優化內存佔用,但直接運行 JS 腳本的性能差於 V8 和 JSC)

針對移動端的垃圾回收策略

6.
優化原理

clipboard.png

截取自code.fb.com

傳統 JavaScript 引擎通常是以上圖的模式完成代碼執行的,編譯階段只完成 babel 轉義和 minify 壓縮,產物還是 JavaScript 腳本,解釋與執行的任務都需要在運行時完成(如 V8 引擎,還會在運行時將 JavaScript 編譯爲本地機器碼)很明顯缺點就是在運行時需要邊解釋邊執行,甚至需要佔用系統資源執行編譯任務。

clipboard.png
取自code.fb.com

Hermes 引擎使用了 aot 編譯的方式,將解釋和編譯過程前置到編譯階段,運行時只完成機器碼的執行,大大提高了運行效率。

7.
已有項目接入 Hermes

  • 升級 React-Native 及相關庫升級(成本較小)
  • 因爲 React-Native 0.60.x 變更爲依賴 AndroidX,所以 Android 項目需要使用 28 以上版本編譯,適配
    Android 高版本,且需要遷移到 AndroidX(成本較大)
  • 修改 build.gradle,添加 Hermes 相關屬性及依賴(成本較小)

8.
是否支持 CodePush?

Hermes 引擎預編譯後的產物與RN原方式相同,都是在 assets 文件夾下生成的 index.android.bundle 文件。RN 原方式中 index.android.bundle 是經過壓縮的 JavaScript 腳本文件,Hermes 預編譯後則是二進制文件。因爲只有產物文件格式的區別,並沒有修改原有JS Bundle 的加載方式,所以 CodePush 可以繼續使用。

目前 code-push 的兩種發佈模式支持情況:

clipboard.png

9.
調試效率

Debug 模式下 Hermes 不開啓預編譯以支持 Hot Reload ,缺點是 Release 模式下所有Hermes 引擎優勢都不存在,甚至因爲無 JIT 導致性能還要差於原有引擎。但開發者模式並不追求性能,而更追求調試效率。

Debug 模式內置 libhermes-inspector.so ,支持 Chrome inspect 的使用,支持 DevTools 協議,比原有 RN 調試體驗更佳(應用內代理,不能同步調試原生調用)

10.
ES 標準支持

Hermes 支持 ES6,緊跟最新的 JavaScript 規範。爲了優化引擎大小,不支持 RN 程序中使用較少的語言特性,如本地 eval()。

11.
性能調研

▍包大小分析

clipboard.png
JSC 引擎 Release 包

clipboard.png
Hermes 引擎 Release 包

原包大小 20MB(JSC)
新包大小 18MB(Hermes)

包大小減小 2MB,整體減少 2MB / 20MB = 10%

分析具體包大小減小的原因可以發現,包內容兩者只有 lib 大小和 assets 的大小存在差異。

clipboard.png
JSC 引擎 Release 包

clipboard.png
Hermes 引擎 Release 包

對比 lib 內容,發現大小差距主要是由 libjsc.so 和 libhermes.so 兩者的差距導致的,即 Hermes 引擎的大小。

clipboard.png
JSC 引擎 Release 包

clipboard.png
Hermes 引擎 Release 包

對比 assets 內容,發現大小變化主要由 index.android.bundle ,即 JavaScript 打包產物引起,Hermes 模式下反而更大的原因是進一步編譯爲二進制代碼。

兩者影響疊加導致整體減小,包大小得到優化。(支持的平臺越多,包體積優化效果越好)

▍內存分析

實驗方法:在相同的業務頁面穩定狀態下通過 Memory Profiler 查看內存佔用情況

clipboard.png
JSC 引擎 Release 包

clipboard.png
Hermes 引擎 Release 包

原包平均內存佔用 210MB
新包平均內存佔用 190MB

內存佔用平均減小20MB以上,整體減小20MB / 210MB = 10%
分析 Profiler 數據可以發現,內存優化主要發生在 Code 內存區。

clipboard.png
JSC 引擎 Release 包

clipboard.png
Hermes 引擎 Release 包

Google 官方文檔中對內存 Code 區的描述:

Code:您的應用用於處理代碼和資源(如 dex 字節碼、已優化或已編譯的 dex 碼、.so 庫和字體)的內存。

聯繫到上個章節中包大小分析中 libhermes.so 尺寸的減小,可以很容易想到,內存佔用的減少就是因爲 .so 對內存佔用的減小。另外兩者對 JavaScript 內存的佔用也有細微差別,但是可以忽略不計。

▍TTI性能

TTI:Time to Interactive,用戶可交互時間,啓動到頁面渲染完成並且可以正常響應用戶的輸入的時間,衡量用戶體驗的移動端指標。

React-Native Android 中主要是 Application onCreate 開始到 RN 組件渲染完成可交互的時間。

值得吐槽的是,在 iOS 版本的 Pref Monitor 中直接就包含了這個指標的顯示,但是 Android 版本的 Pref Monitor 只有四個指標,且並沒有 TTI 這一指標。

在 Android 平臺上可以通過 RN 提供的 ReactFindViewUtil 類獲取 RN 組件對應的原生組件,註冊對應的渲染回調,在控件渲染完成時記錄TTI結束時間。

clipboard.png
JSC 引擎 Release 包

clipboard.png
Hermes 引擎 Release 包

原包 TTI 829ms
新包 TTI 694ms

TTI 減少 135ms,整體減少 135ms / 829ms = 16%

12.
總結

面對 Flutter 的咄咄攻勢,React-Native 終於做出了一些改變,Hermes 作爲一款適合移動端的 JavaScript 引擎,確實有其性能優勢,希望通過本文能夠讓你更加了解 Hermes。

本文首發自普惠出行產品技術 (ID:pzcxtech)

圖片描述

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