RN 技术探索:Hermes Engine 初探

自从 Google 的 Flutter 发布以后,Facebook 对 React-Native 的迭代开始快了起来,优化 React-Native 的性能表现,避免被 Flutter 比下去。最近一个比较大的动做是开源了一个 JavaScript 引擎,并将其包含到 React-Native 中。那么这款引擎它有什么不一样,相比 V八、JSC 这些 JavaScript 引擎又有什么优点呢,如今本文来为你揭晓。android

1.Hermes 引擎是什么,优点有哪些?

重要的事情提早说:Hermes 引擎是 Facebook 研发,在 React-Native Android 端用于替换 JavaScript Core 的 JavaScript 引擎。Hermes 引擎的优点是适合移动端的轻量级 JavaScript 引擎,使用 aot 编译,能够减小 Android 端内存使用,减少安装包大小,提高执行效率。web

2.什么是 JavaScript 引擎?

JavaScript 引擎是一个专门处理 JavaScript 脚本的虚拟机,通常会附带在网页浏览器之中。浏览器

3.主流 JavaScript 引擎

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

4.RN 中的 JavaScript 引擎

Weex,Android:V8,iOS:JavaScriptCoreide

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

注:Hermes Engine在React-native 0.60.2 版本后支持性能

5.Hermes 的特点

预编译字节码(引擎加载二进制代码效率高于运行JS脚本)字体

无 JIT 编译器(减少了引擎大小,优化内存占用,但直接运行 JS 脚本的性能差于 V8 和 JSC)gradle

针对移动端的垃圾回收策略优化

6.优化原理

在这里插入图片描述截取自code.fb.com

传统 JavaScript 引擎一般是以上图的模式完成代码执行的,编译阶段只完成 babel 转义和 minify 压缩,产物仍是 JavaScript 脚本,解释与执行的任务都须要在运行时完成(如 V8 引擎,还会在运行时将 JavaScript 编译为本地机器码)很明显缺点就是在运行时须要边解释边执行,甚至须要占用系统资源执行编译任务。
在这里插入图片描述

截取自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 的两种发布模式支持状况:
在这里插入图片描述

9.调试效率

Debug 模式下 Hermes 不开启预编译以支持 Hot Reload ,缺点是 Release 模式下全部Hermes 引擎优点都不存在,甚至由于无 JIT 致使性能还要差于原有引擎。但开发者模式并不追求性能,而更追求调试效率。

Debug 模式内置 libhermes-inspector.so ,支持 Chrome inspect 的使用,支持 DevTools 协议,比原有 RN 调试体验更佳(应用内代理,不能同步调试原生调用)

10.ES 标准支持

Hermes 支持 ES6,紧跟最新的 JavaScript 规范。为了优化引擎大小,不支持 RN 程序中使用较少的语言特性,如本地 eval()。

11.性能调研

▍包大小分析
JSC 引擎 Release 包
JSC 引擎 Release 包

在这里插入图片描述
Hermes 引擎 Release 包

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

包大小减少 2MB,总体减小 2MB / 20MB = 10%

分析具体包大小减少的缘由能够发现,包内容二者只有 lib 大小和 assets 的大小存在差别。
在这里插入图片描述
JSC 引擎 Release 包

在这里插入图片描述
Hermes 引擎 Release 包

对比 lib 内容,发现大小差距主要是由 libjsc.solibhermes.so 二者的差距致使的,即 Hermes 引擎的大小。

在这里插入图片描述
JSC 引擎 Release 包

在这里插入图片描述Hermes 引擎 Release 包

对比 assets 内容,发现大小变化主要由 index.android.bundle ,即 JavaScript 打包产物引发,Hermes 模式下反而更大的缘由是进一步编译为二进制代码。

二者影响叠加致使总体减少,包大小获得优化。(支持的平台越多,包体积优化效果越好)

▍内存分析

实验方法:在相同的业务页面稳定状态下经过 Memory Profiler 查看内存占用状况

在这里插入图片描述
JSC 引擎 Release 包

在这里插入图片描述
Hermes 引擎 Release 包

原包平均内存占用 210MB
新包平均内存占用 190MB

内存占用平均减少20MB以上,总体减少20MB / 210MB = 10%
分析 Profiler 数据能够发现,内存优化主要发生在 Code 内存区。

在这里插入图片描述
JSC 引擎 Release 包

在这里插入图片描述
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结束时间。


JSC 引擎 Release 包

在这里插入图片描述
Hermes 引擎 Release 包

原包 TTI 829ms
新包 TTI 694ms

TTI 减小 135ms,总体减小 135ms / 829ms = 16%

12.总结

面对 Flutter 的咄咄攻势,React-Native 终于作出了一些改变,Hermes 做为一款适合移动端的 JavaScript 引擎,确实有其性能优点,但愿经过本文可以让你更加了解 Hermes。

本文首发自普惠出行产品技术 (ID:pzcxtech)
在这里插入图片描述