写点什么

加载速度提升 15%,携程对 RN 新一代 JS 引擎 Hermes 的调研

  • 2019-09-05
  • 本文字数:4669 字

    阅读完需:约 15 分钟

加载速度提升15%,携程对RN新一代JS引擎Hermes的调研

引言

Facebook 在 ChainReact2019 大会上正式推出了新一代 JavaScript 执行引擎 Hermes。Hermes 是个轻量级的 JS 引擎,专门对 Android 上运行 ReactNative 进行了优化。我们第一时间在 CRN 项目中集成了 Hermes, 并做了深度调研。

一、Hermes 介绍

自 ReactNative 推出以来,有大量的 APP 接入并使用,其中也包括大型应用的主流程业务。随着业务复杂度不断上升,性能问题变得无法忽视。


在分析性能数据时,Facebook 团队发现 JavaScript 引擎是影响启动性能和应用包体积的重要因素。由于 JavaScriptCore 最初是为桌面浏览器端设计,相较于桌面端,移动端能力有太多的限制,为了能从底层对移动端进行性能优化,Facebook 团队选择自建 JavaScrip 引擎,设计了 Hermes,限于 iOS AppStore 审核限制,目前仅用于 Android 平台。


Chain React 大会上官方给出了 Hermes 引擎一组数据:


  • 从页面启动到用户可操作的时间长短(Time To Interact:TTI),从 4.3s 减少到 2.01s

  • App 的下载大小,从 41MB 减少到 22MB

  • 内存占用,从 185MB 减少到 136MB


CRN 先前做过框架代码拆分和预加载、业务代码懒加载、业务代码预加载等性能优化方案,正困惑于如何更近一步进行性能优化。当看到 Hermes 这三个关键指标都有了显著的提高,非常激动,觉得 Hermes 是非常好的一个方向,接下来我们就来了解 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.jsThis 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.hbcThis is Hermes Demo
复制代码

三、Hermes 是如何优化的?

主流 JavaScript 引擎,例如 JSC、V8、SpiderMonkey 等几乎都是为了桌面端浏览器服务的,Hermes 针对移动终端设备的特点做了一些优化,其中最重要的我们认为是以下两点:

3.1 字节码预编译

现代主流的 JavaScript 引擎在执行一段 js 代码的大概流程是:


  • 先读取源码文件

  • 解析源代码并转换成字节码(bytecode)

  • 最后执行


在运行时解析源码转换字节码是一种时间浪费,所以 Hermes 选择预编译的方式在编译期间生成字节码。这样做一方面避免了不必要的转换时间,另一方面多出的时间可以用来优化字节码,从而提高执行效率。


3.2 放弃 JIT

为了加快执行效率,现在主流的 JavaScript 引擎都会使用一个 JIT 编译器在运行时通过转换成机器码的方式优化 JS 代码。Faceback 团队认为 JIT 编译器有主要俩个问题:


  • 要在启动时候预热,对启动时间有影响;

  • 会增加引擎 size 大小和运行时内存消耗;


基于这俩点对性能指标的影响,Faceback 团队决定不实现 JIT 编译器。


这里所谓放弃 JIT,有两点需要再解释一下:


  • 纯文本 JS 代码执行效率降低。放弃 JIT,是指放弃运行时 Hermes 引擎对纯文本 JS 代码的编译优化。我们的验证数据也表面,纯文本的 JS 代码执行,Hermes 引擎明显比 JavaScriptCore 慢。

  • 对 RN 代码的动态性无影响。由于 Hermes 仍然可以执行纯文本的 JS 代码,并且可以支持动态读取 bytecode, 因此对 RN 的动态性并无影响。

四、如何集成 Hermes?

4.1 从新创建工程集成

1. 升级最新react-native-clinpm install -g react-native-cli
2.初始化最新react-native工程,最新版为0.60.3react-native init HermesDemo
3. 开启hermes, 编辑HermesDemo工程 android/app/build.gradl文件 project.ext.react = [ entryFile: "index.js",- enableHermes: false // clean and rebuild if changing+ enableHermes: true // clean and rebuild if changing ]
4. 使用Relase包体验Hermes带来的速度提升react-native run-android --variant release
复制代码

4.2 从源码集成

git clone https://github.com/facebook/react-native.git // 需要切换到Hermes release节点,比如:eec4dc6cd react-nativenpm install./gradlew :RNTester:android:app:installHermesRelease // 使用生产环境hermes
复制代码

4.3 Hermes 集成过程分析

分析 react-native react.gradle 源码可以看到,如果打开了 Hermes 开关,会在原先打包 RN 代码的 bundleXXXJsAndAsset task 后面追加执行一段 Hermes 转换命令: hermes --emit-binary -out xxx。


...// 1. 执行标准RN打包commandLine(*nodeExecutableAndArgs, cliPath, bundleCommand, "--platform", "android", "--dev", "${devEnabled}",                    "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir,                    "--sourcemap-output", jsPackagerSourceMapFile, *extraArgs)......// 2. 将打包好的jsbundle文件转换成字节码if (enableHermes) {    commandLine(getHermesCommand(), "-emit-binary", "-out", jsBundleFile, jsBundleFile, *hermesFlags)}...
复制代码

4.4 执行过程分析

为了进一步抽象 JavaScript 执行层,RN 底层创建了 JSExecutor 和 Runtime 接口,并把大部分业务逻辑放到了实现了 JSExecutor 的 JSIExcutor.cpp 中。对于 JavaScript 执行引擎来说只需要实现 Runtime 接口即可对接 RN 框架。


JavaScriptCore 的 Runtime 实现类是 JSCRuntime。相应的,此次 Hermes 升级,底层创建了 HermesRuntime。


// JSCRuntime.cpp jsc Runtimeclass JSCRuntime : public jsi::Runtime
// hermes.h hermes Runtimeclass HermesRuntime : public jsi::Runtime...
复制代码


每一种 JSExecutor 都提供了创建类 XXXExecutorFactory 来创建相应实例,并且提供了相应的 Java 对象。


RN 框架在初始化 ReactInstanceManager 的时候需要传入 JavaScriptExecutorFactory。如果要切换 JavaScript 执行引擎只需要在 ReactInstanceManager 创建的时候做控制即可。


官方的控制流程是,优先加载 jscexecutorso,如果成功则使用 JSCRuntime,否则使用 HermesRuntime。


private JavaScriptExecutorFactory getDefaultJSExecutorFactory(String appName, String deviceName) {    try {      // If JSC is included, use it as normal      SoLoader.loadLibrary("jscexecutor");      return new JSCExecutorFactory(appName, deviceName);    } catch(UnsatisfiedLinkError jscE) {      // Otherwise use Hermes      return new HermesExecutorFactory();    }  }
复制代码


由此可见无论是对于 RN JS 代码的打包还是 Native 代码逻辑的更改,升级 Hermes 的成本都非常低。

五、Hermes,JavaScriptCore,V8 的对比

通过上面的 Hermes 集成分析可知,Hermes 对整个 RN 原有架构的侵入是极少的,甚至做到了可插拔式接入。我们很快将 Hermes 集成到携程 CRN 框架,并和原先的 JavaScriptCore 引擎以及社区提供的 V8 引擎做了比较。


经过我们的数据验证,Faceback 团队提出的关键性指标相较于原先的 JSC 都有了显著提高。


  • 首屏渲染速度:bytecode 代码执行情况下,Hermes 比 JavaScriptCore 要快。在携程 App 中,拿门票业务做了验证,在做了预加载的情况下,首屏加载速度依然可以提升约 15%。而 V8 的表现就非常糟糕了。

  • Native so size:RN 所依赖的必要 so 库,Hermes 比 JavaScriptCore 减少了约 16%(单 armeabi 架构压缩后降低了 0.5M 左右),V8 则要远大于 Hermes 和 JavaScriptCore。



  • 内存:拿RNTester工程测试进入 RN 页面滑动进入若干页面并退出之后,内存的波动情况比较可以看到,V8 和 Hermes 内存增长要更加平滑。



  • CPU:拿RNTester工程测试进入 RN 页面滑动进入若干页面并退出之后,对比 CPU 波动情况。Hermes 明显好于 V8 和 JavaScriptCore。


六、Hermes 引擎的动态性

另外通过我们的测试,Hermes 在执行字节码和文本 JS 上有一些很有意思的特性,这些特性让升级成本变得非常低:


  • Hermes 支持执行纯文本的 js

  • 支持动态加载纯文本 js 或者 bytecode

  • 支持 bytecode 和纯文本 js 混合使用:比如 a.hbc 是 bytecode,模块中引用了 b.js,b 模块是纯文本 js。在加载的时候可以先加载 a.hbc 文件,然后加载 b.js 文件。可正常执行。

七、Hermes 目前的问题

Hermes 诸多优点让我们团队非常兴奋,几乎觉得应该立马把 JavaScriptCore 下掉,更换至 Hermes。但随着测试和集成的进行,Hermes 带来的问题逐渐显现。

7.1 bytecode 文件占用 size 过大问题

Hermes 编译的字节码文件比纯文本 js 文件增大 100%。


携程旅行 App 的安装包中有 20MB(7z 压缩后)左右的 RN 业务代码,如果都编译成 bytecode,将会再增加 20MB 大小,这是无法接受的。另外,动态下发 RN 增量包时,由于是二进制文件 diff,差分效率极低。


为了解决这个问题,我们根据 Hermes 的特性,转变思路,将 Hermes 的 bytecode 编译放到客户端去做,客户端同时存储 js 和 bytecode 文件,如果有 bytecode 编译完成则使用 Hermes,否则仍然使用 JavaScriptCore。


Hermes 开源项目提供了编译 bytecode 的 complieJS 方法,但这部分代码没有默认打包到 RN 的 Hermes 引擎中,我们稍加整合、封装,通过 JNI 暴露出来,供业务使用。


拿最大的 RN 业务包(1100 个文件,6.5MB 大小),做测试,后台线程执行,小米 9 Android10 耗时 2.49 秒;三星 S6edge+ android 7.0 耗时 6 秒。由于 bytecode 不是必须,因此该耗时尚可接受。

7.2 执行纯文本 js 耗时长

在客户端将纯文本 js 转换成 bytecode 之前,我们让 Hermes 加载纯文本。但实际测试下来,发现 Hermes 加载纯文本的性能比 JavaScriptCore 要慢将近 30%。主要原因是 Hermes 删除 JIT 功能,致使对纯文本 js 代码运行变慢。

7.3 缓存问题

我们对原生 RN 框架做了大量的优化,缓存使用过的 JS 执行引擎是优化过程非常重要的一环。


拿门票页面举例来说,如果用户启动 App,第一次进入门票业务将会使用一个全新的 JavaScript 引擎并从磁盘读取文件、加载文件、执行 JS 代码。用户退出门票页面之后该引擎被缓存,如果用户再一次进入将会使用缓存的引擎,不用重新读取、加载和执行,仅仅需要创建相关 JS 对象并渲染即可。


遗憾的是,测试 Hermes 的缓存的时候,我们发现使用缓存的 Hermes 引擎加载业务代码表现非常一般,甚至某些情况下比第一次加载还要慢。而使用缓存的 JavaScriptCore 引擎,第二次打开页面的速度与打开纯 native 页面的速度几乎相当,并且表现相当稳定。



为什么使用缓存的 Hermes 引擎打开页面速度不理想,可能和 Hermes 的设计有关,我们还在进一步分析中。

八、总结与展望

  • 从目前情况来看,在解决缓存问题之前,我们无法在线上版本直接引入 Hermes。

  • 解决缓存问题之后,可以采用 JavaScriptCore+Hermes 双引擎。通过客户端转换 bytecode 字节码。使用 jsc 加载优化之前的纯文本 js,一旦优化完毕切换至 Hermes 引擎。

  • 另外如果使用 Hermes 引擎我们需要充分测试稳定性和兼容性。

  • Hermes 通过预编译字节码的方式提升 js 执行速度,给了我们新的思路。我们也正在调研 JavaScriptCore 或者 V8 的 bytecode 在移动端的支持度,性能和兼容性。


作者介绍


储贻锋,携程无线平台研发部基础框架组资深 Android 研发,目前主要负责 CRN Android 端和携程 Android 基础架构的维护与开发工作。


本文转载自公众号携程技术中心(ID:ctriptech)


原文链接


https://mp.weixin.qq.com/s/BOeuLoZjCdi61P_MhaJT0g


2019-09-05 08:004108

评论 1 条评论

发布
用户头像
峰哥牛啊
2021-05-19 17:43
回复
没有更多了
发现更多内容

【京东云新品发布月刊】2024年7月产品动态

京东科技开发者

某黑产最新免杀攻击样本详细分析

我再BUG界嘎嘎乱杀

黑客 网络安全 安全 网安

产品经理的需求管理秘籍:9大工具解读

爱吃小舅的鱼

需求管理 软件需求管理 需求管理工具

微软将深度整合 Azure AI 与 GitHub;Stability AI 0.5 秒生成三维建模丨 RTE 开发者日报

声网

打造知识共享文化:2024年8大最佳企业工具

爱吃小舅的鱼

知识管理 知识管理系统 知识管理软件

MelosBoom 智能硬件音响:推动全民 DePIN 的全新时代

股市老人

电车风噪大?巧妙利用空气动力学和仿真技术,解决风噪影响

Altair RapidMiner

汽车 仿真 汽车仿真 altair 新能源车

如何在Java、C、Ruby语言中使用Newscatcher API

幂简集成

ruby C语言 API java

前端常用的几个工具网站

源字节1号

开源

开启智能屏跃级体验,天猫精灵「哇哦闺蜜机」首发 4999 元起

新消费日报

聊聊JVM如何优化

京东科技开发者

开放签电子签章系统终于支持docker部署方式了

开放签开源电子签章

Docker 镜像 电子签章

TapData 信创数据源 | 国产信创数据库 OceanBase 数据同步指南,加速国产化进程,推进自主创新建设

tapdata

制造业数字化转型创新思路 |《数智新时代制造业数字化创新实践白皮书》上线!

tapdata

中国AI长卷(三):算法生根

脑极体

蚌埠住了!我把斯坦福大牛的Web安全开发指南给分享出来了!

我再BUG界嘎嘎乱杀

黑客 网络安全 信息安全 WEB安全 网安

JavaScript 中的闭包和事件委托

不在线第一只蜗牛

JavaScript 前端

开发体育赛事转播软件平台的成功之道:确定目标市场的需求

软件开发-梦幻运营部

Netty的源码分析和业务场景

快乐非自愿限量之名

php Netty 开发语言

基于Golang实现Kubernetes边车模式

俞凡

golang 架构 微服务

唯品会商品列表数据接口详解与实战应用(VIP.item_search)

tbapi

唯品会商品列表数据接口 唯品会API接口 唯品会

123131

一鸣(Ming)

基于人工智能的代码分析与 Bug 检测实战

霍格沃兹测试开发学社

面试官:说说你的项目亮点?

王磊

Java

天润融通助力车企做好战败线索分析,实现商机转化最大化

天润融通

人工智能

工程化实践:工程配置化设计

XIAOJUSURVEY

开源 配置化 工程化

三星Galaxy Z Flip6:内外兼修小折花魁,解锁科技改变生活新方式

科技热闻

JAVA应用CPU跳点自动DUMP工具

京东科技开发者

AI大模型在业务受理的智能化实践和探索

鲸品堂

运营商 大模型 企业服务大模型

Milvus 向量数据库进阶系列丨构建 RAG 多租户/多用户系统 (上)

Zilliz

人工智能 AI Milvus 向量数据库 大语言模型

工程架构简析

XIAOJUSURVEY

开源 全栈 问卷架构

加载速度提升15%,携程对RN新一代JS引擎Hermes的调研_技术管理_储贻锋_InfoQ精选文章