前端优化系列 - JS解析性能分析
前言
通常,我们觉得页面已经写得非常好,但性能却不尽如人意,在Trace就看到一大堆JS在执行,却不知在执行什么逻辑。JS执行为什么会这么耗时,它们到底在执行什么逻辑呢?
本文尝试从各个维度详细解析JS的性能消耗情况,找出导致页面性能大幅下降的真正杀手。
JS性能
一般来说,页面资源的性能消耗包括加载和执行。在加载方面,各类资源基本是平等的,主要与资源大小和网络有关。
在执行方面,差异就非常大,比如,
(1)图片的解码和渲染,可能在十毫秒级就处理完了;
(2)CSS解析,样式重计算,排版,估计在百毫秒级也可以处理完;
(3)而JS的Parse/Compile + JS Execution,可能好几秒才能完成。
关于JS的性能消耗,已经有非常优秀的文档,详细请参考:The Cost Of JavaScript。
我们先来看一个页面完整的JS执行情况,
从上图可以看到,页面的JS执行(v8.run)占了绝大部分时间,甚至单个JS执行超过2.8秒。那么,这些JS执行的消耗到底在哪里?是在执行业务逻辑,还是在干别的事情?
我们先看看一组基准测试的数据,
按Octane基准测试,70%以上的时间消耗在JS Execution,而Parse/Compile 占比不到10%。
按Speedometer测试,Parse/Compile的耗时超过35%,而JS Execution的时间却很小。
这两类测试的数据并不一致,甚至完全相反。那么,那个测试更加准确呢?
很多数据表明,Speedometer测试能够更加准确的反映真实页面的性能,详细请参考:V8如何度量真实数据性能
JS解析
越来越多的数据表明,JS解析是JS性能消耗的主要部分。JS解析编译为什么会很耗时呢?其实,高级语言(C++)的编译更加耗时,一般来说,生成代码的优化程度越高解析编译过程就越耗时。
JS解析的耗时大概是怎样的呢?一般来说,在Nexus5手机上,生成100K的字节码需要消耗100-200ms的JS解析时间。
为什么要说明具体的测试机器呢,因为CPU对JS解析的影响非常大,iPhone8的性能可能是Nexus5的好几倍,所以一般应该选择中低端机器去进行测试。
那么,为什么现在JS解析会成为页面性能瓶颈呢?
我们先看看页面的平均JS大小是怎样的呢?根据HttpArchive统计,Nov 15,2010 页面平均JS大小为113K,而在 Nov 15,2017 页面平均JS大小为459K,其中top 1000站点的平均JS大小为617K,即JS大小一直在大幅增长。这些大小是指网络传输的大小,一般资源会经过gzip压缩再进行传输,所以资源的原始大小可能会更大,而ByteCode字节码的大小与原始大小接近。
从这些数据我们可以推断,页面的平均JS大小在不断增长,目前消耗在JS解析的时间已经非常惊人,全网平均时间会超过500ms。
我们再看看一些比较流行的前端框架的情况,现在基本每个页面都会引入一套前端框架,Angular,React,Vue 的大小情况如下,
(1)angular2 gzip大小111K,原始大小566K
111K Jan 4 22:11 angular2.min.js.gz
566K Jan 4 22:03 angular2.min.js
(2)angular.1.4.5 gzip大小51K,原始大小143K
51K Jan 4 22:11 angular.1.4.5.min.js.gz
143K Jan 4 21:46 angular.1.4.5.min.js
(3)react-0.14.5 gzip大小39K,原始大小132K
39K Jan 4 22:11 react-0.14.5.min.js.gz
132K Jan 4 21:56 react-0.14.5.min.js
(4)vue-2.0.3 gzip大小23K,原始大小63K
23K Oct 13 03:02 vue-2.0.3.min.js.gz
63K Oct 13 03:02 vue-2.0.3.min.js
如果再加上一系列的依赖库会更大,知乎上的文章 提到react.js 加上依赖库,大小可达600K。
是不是JS框架文件越小性能越好?当然不是,良好的性能还需要框架和业务JS代码写得非常好。但是,如果JS框架文件非常大,JS解析的时间就决定了它的性能不会很好。比如,在首屏引入了一个600K的框架,就等同于在Nexus5手机上引入了600ms以上的性能损耗。
V8 Cache
JS解析这么耗时,为什么不将解析的结果缓存起来呢?的确如此,JS解析的ByteCode字节码是可以缓存的。那为什么不缓存执行效率更高的机器码呢?很多研究表明,缓存机器码的实际效果并不好。
Chrome V8在是否生成ByteCode字节码的过程,是走过弯路的。Chrome V8在2010年开始就采用双编译的架构,在2015年加入新的编译器TurboFan,全力去优化生成代码的质量。但这种架构,仅仅在实验室跑分上占优,在真实页面的性能上远远落后于JSC引擎。
2016年之后,Chrome V8团队成员进行了反思,引入了新的解析器Ignition,使用解析器+编译器的架构(Ignition + TurboFan),并于Chrome 59版本默认打开,新的架构带来的收益非常明显,JS占用的内存大幅下降而实际页面的性能大幅提升。
那么,在真实世界上使用V8 Cache的效果是怎样的呢?
上图是U4 2.0使用V8 Cache之前的Trace,JS执行耗时2826ms。(注:U4 2.0 是UC浏览器基于Chromium Blink打造的新一代浏览器内核)
上图是同一个JS,使用U4 2.0的V8 Cache之后的效果,JS执行耗时797ms,降到了原来的1/4,这个收益是非常明显的。
对很多页面来说,很有可能年度的性能优化目标,通过升级U4 2.0就可以实现了。
U4 2.0的V8 Cache这么美好,在什么条件下才能用上呢?
V8 Cache是根据JS文件的二进制内容生成,只要JS文件的二进制内容没有变化,都是可以用上的,包括页面关闭,浏览器重启,甚至是手机重启。特别说明一下,多个进程也可以共享同样的V8 Cache。也就是说,唯一会让V8 Cache失效的是JS文件二进制内容发生了变化。
为什么JS文件的内容会发生变化呢?一些容器在进行JS注入时,会往JS文本插入时间戳,这样就用不上V8 Cache了。
浏览器是否会自动清理V8 Cache呢?当然会的,因为V8 Cache文件还是挺大的,如果无限制,很容易会占满用户的存储空间。
结束语
在前端渲染非常流行的今天,页面的大部分逻辑都会通过JS去实现,复杂的业务逻辑让完全从头开始写一套JS代码变得几乎不可能,越来越多的JS框架被引进各类页面,页面的JS正变得越来越大。我们必须清醒的认识到,每100K的JS源代码可能意味着在中低端机器上100-200ms的性能损耗。
在JS性能方面,我们给的建议是,
(1)降低页面JS的大小,包括依赖库的JS大小,减少不必要的依赖。
(2)基于Vue,React,Angular,等JS框架的核心库去进行开发,不要一股脑引入一大堆依赖库。
(3)拆分首屏与非首屏逻辑,让执行耗时的JS尽可能在首屏之后执行。
(4)关注JS引擎的升级,JS引擎也在持续优化。对于阿里系的应用来说,需要关注UC内核的升级。
参考文档
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
安装好Pycharm后如何配置Python解释器简易教程
这两天有许多Python小白加入学习群,并且问了许多关于Pycharm基本使用的问题,今天小编就以配置Python解释器的问题给大家简单絮叨一下。 1、一般来说,当我们启动Pycharm,如果Pycharm正常激活的话,就会直接进入到Pycharm中去,并且Pycharm经常会弹出下图的界面。(如果有小伙伴的Pycharm尚未激活,可以站内私信,小编这有好几个激活码,给大家分享。) 其实这个是Pycharm的提示信息,一般是Pycharm的简易或快捷方式介绍或者其他的Pycharm功能说明,大家完全可以不用理会,直接点击右下方的“Close”即可,不会对你产生任何影响。 2、之后我们可能会碰到一个问题,先后有好几个小伙伴咨询Pycharm安装后并不能直接写代码,并且有图为证,如下图所示。 一看到这个提示,就知道Pycharm中尚未配置Python解释器,此时不用慌,并不是Pycharm没有安装成功,而是因为有个配置尚未完成,只需要配置好Python解释器之后,一切都会正常。其实Python解释器的配置并不难,具体的教程如下。 3、首先点击上图中“Configure Python Int...
- 下一篇
Thrift简明教程
Thrift是个RPC框架,它的目标是为各种流行的开发语言提供便利的RPC调用机制,而不需要使用那些开销巨大的方式,比如SOAP。 Thrift使用语言中立的服务定义文件(IDL,接口描述语言),描述数据类型和服务接口。这个文件会被用作引擎的输入,编译器生成代码为每种支持的语言生成RPC客户端代码库。这种静态生成的设计让它非常容易被开发者所使用,而且因为类型验证都发生在编译期而非运行期,所以代码可以很有效率地运行。 Apache Thrift,它采用接口描述语言(IDL)定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码生成引擎可以在多种语言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk 等创建高效的、无缝的服务,其传输数据采用二进制格式,相对 XML 和 JSON 体积更小,对于高并发、大数据量和多语言的环境更有优势。 Thrift 服务开发流程,简单概述如下: 1、定义IDL文件(xxx.thrift文件) 2、用xxx.thrift文件生成Java代码(服务接口文件)...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS关闭SELinux安全模块
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS6,CentOS7官方镜像安装Oracle11G
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS8编译安装MySQL8.0.19
- CentOS6,7,8上安装Nginx,支持https2.0的开启