设为首页 加入收藏

TOP

JavaScript 启动性能瓶颈分析与解决方案(一)
2017-02-21 08:16:08 】 浏览:534
Tags:JavaScript 启动 性能 瓶颈 分析 解决方案

随着现代 Web 技术的发展与用户交互复杂度的增加,我们的网站变得日益臃肿,也要求着我们不断地优化网站性能以保证友好的用户体验。本文作者则着眼于 java script 启动阶段优化,首先以大量的数据分析阐述了语法分析、编译等步骤耗时占比过多是很多网站的性能瓶颈之一。然后作者提供了一系列用于在现代浏览器中进行性能评测的工具,还分别从开发者工程实践与 java script 引擎内部实现的角度阐述了应当如何提高解析与编译速度。



在 Web 开发中,随着需求的增加与代码库的扩张,我们最终发布的 Web 页面也逐渐膨胀。不过这种膨胀远不止意味着占据更多的传输带宽,其还意味着用户浏览网页时可能更差劲的性能体验。浏览器在下载完某个页面依赖的脚本之后,其还需要经过语法分析、解释与运行这些步骤。而本文则会深入分析浏览器对于 java script 的这些处理流程,挖掘出那些影响你应用启动时间的罪魁祸首,并且根据我个人的经验提出相对应的解决方案。回顾过去,我们还没有专门地考虑过如何去优化 java script 解析/编译这些步骤;我们预想中的是解析器在发现 <script> 标签后会瞬时完成解析操作,不过这很明显是痴人说梦。下图是对于 V8 引擎工作原理的概述:



下面我们深入其中的关键步骤进行分析。


在启动阶段,语法分析,编译与脚本执行占据了 java script 引擎运行的绝大部分时间。换言之,这些过程造成的延迟会真实地反应到用户可交互时延上;譬如用户已经看到了某个按钮,但是要好几秒之后才能真正地去点击操作,这一点会大大影响用户体验。



上图是我们使用 Chrome Canary 内置的 V8 RunTime Call Stats 对于某个网站的分析结果;需要注意的是桌面浏览器中语法解析与编译占用的时间还是蛮长的,而在移动端中占用的时间则更长。实际上,对于 Facebook, Wikipedia, Reddit 这些大型网站中语法解析与编译所占的时间也不容忽视:



上图中的粉色区域表示花费在 V8 与 Blink's C++ 中的时间,而橙色和黄色分别表示语法解析与编译的时间占比。Facebook 的 Sebastian Markbage 与 Google 的 Rob Wormald 也都在 Twitter 发文表示过 java script 的语法解析时间过长已经成为了不可忽视的问题,后者还表示这也是 Angular 启动时主要的消耗之一。



随着移动端浪潮的涌来,我们不得不面对一个残酷的事实:移动端对于相同包体的解析与编译过程要花费相当于桌面浏览器2~5倍的时间。当然,对于高配的 iPhone 或者 Pixel 这样的手机相较于 Moto G4 这样的中配手机表现会好很多;这一点提醒我们在测试的时候不能仅用身边那些高配的手机,而应该中高低配兼顾:



上图是部分桌面浏览器与移动端浏览器对于 1MB 的 java script 包体进行解析的时间对比,显而易见的可以发现不同配置的移动端手机之间的巨大差异。当我们应用包体已经非常巨大的时候,使用一些现代的打包技巧,譬如代码分割,TreeShaking,Service Workder 缓存等等会对启动时间有很大的影响。另一个角度来看,即使是小模块,你代码写的很糟或者使用了很糟的依赖库都会导致你的主线程花费大量的时间在编译或者冗余的函数调用中。我们必须要清醒地认识到全面评测以挖掘出真正性能瓶颈的重要性。


我曾不止一次听到有人说,我又不是 Facebook,你说的 java script 语法解析与编译到


底会对其他网站造成什么样的影响呢?对于这个问题我也很好奇,于是我花费了两个月的时间对于超过 6000 个网站进行分析;这些网站囊括了 React,Angular,Ember,Vue 这些流行的框架或者库。大部分的测试是基于 WebPageTest 进行的,因此你可以很方便地重现这些测试结果。 光纤接入的桌面浏览器大概需要 8 秒的时间才能允许用户交互,而 3G 环境下的 Moto G4 大概需要 16 秒 才能允许用户交互。



大部分应用在桌面浏览器中会耗费约 4 秒的时间进行 java script 启动阶段(语法解析、编译、执行):



而在移动端浏览器中,大概要花费额外 36% 的时间来进行语法解析:



另外,统计显示并不是所有的网站都甩给用户一个庞大的 JS 包体,用户下载的经过 Gzip 压缩的平均包体大小是 410KB,这一点与 HTTPArchive 之前发布的 420KB 的数据基本一致。不过最差劲的网站则是直接甩了 10MB 的脚本给用户,简直可怕。



通过上面的统计我们可以发现,包体体积固然重要,但是其并非唯一因素,语法解析与编译的耗时也不一定随着包体体积的增长而线性增长。总体而言小的 java script 包体是会加载地更快(忽略浏览器、设备与网络连接的差异),但是同样 200KB 的大小,不同开发者的包体在语法解析、编译上的时间却是天差地别,不可同日而语。


打开 Timeline( Performance panel ) > Bottom-Up/Call Tree/Event Log 就会显示出当前网站在语法解析/编译上的时间占比。如果你希望得到更完整的信息,那么可以打开 V8 的 Runtime Call Stats。在 Canary 中,其位于 Timeline 的 Experims > V8 Runtime Call Stats 下。



打开 about:tracing 页面,Chrome 提供的底层的追踪工具允许我们使用 disabled-by-default-v8.runtime_stats 来深度了解 V8 的时间消耗情况。V8 也提供了 详细的指南 来介绍如何使用这个功能。




WebPageTest 中 Processing Breakdown 页面在我们启用 Chrome > Capture Dev Tools Timeline 时会自动记录 V8 编译、eva luateScript 以及 FunctionCall 的时间。我们同样可以通过指明 disabled-by-default-v8.runtime_stats 的方式来启用 Runtime Call Stats。



更多使用说明参考我的 gist


我们还可以使用 Nolan Lawson 推荐的 User Timing API 来评估语法解析的时间。不过这种方式可能会受 V8 预解析过程的影响,我们可以借鉴 Nolan 在 optimize-js 评测中的方式,在脚本的尾部添加随机字符串来解决这个问题。我基于 Google Analytics 使用相似的方式来评估真实用户与设备访问网站时候的解析时间:



Etsy 的 DeviceTiming 工具能够模拟某些受限环境来评估页面的语法解析与执行时间。其将本地脚本包裹在了某个仪表工具代码内从而使我们的页面能够模拟从不同的设备中访问。可以阅读 Daniel Espeset 的 Benchmarking JS Parsing and Execution on Mobile Devices 一文来了解更详细的使用方式。



如果你的 java script 框架支持 AOT(ahead-of-time)编译模式,那么能够有效地减少解析与编译的时间。Angular 应用就受益于这种模式:



不用灰心,你并不是唯一纠结于如何提升启动时间的人,我们 V8 团队也一直在努力。我们发现之前的某个评测工具 Octane 是个不错的对于真实场景的模拟,它在微型框架与冷启动方面很符合真实的用户习惯。而基于这些工具,V8 团队在过去的工作中也实现了大约 25% 的启动性能提升:



本部分我们就会对过去几年中我们使用的提升语法

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Java虚拟机收集器之老年代收集器 下一篇Spring MVC中的HandlerMapping与H..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目