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…

相关文章
相关标签/搜索