写在前面
本文第一部分翻译自Chrome DevTools文档:How to Use the Timeline Tool
去年也做过这个事情,见Chrome DevTools,Chrome版本和文档都在持续更新中,Timeline面板功能更强大了,布局也发生了一些变化
一.译文
用Chrome DevTools的Timeline面板可以记录分析应用运行时的所有活动,可以用来调查应用的性能问题
内容摘要
生成时间轴记录来分析页面载入之后或者用户交互之后发生的所有事件
在概览窗格中查看FPS、CPU和网络请求
点击火焰图(flame chart)中的每个事件可以查看其详细信息
可以放大记录中的一部分以便分析
Timeline面板概述
Timeline面板从上到下依次为:
控制选项
开始记录、停止记录、配置记录过程中抓取哪些信息
概览
对页面性能的高度总结,更多内容在下面
火焰图
CPU堆栈跟踪的可视化表示
在火焰图上可能会看到3条竖直虚线,蓝色线表示
DOMContentLoaded
事件,绿线表示第一次绘制的时间点,红色表示load
事件详细信息
选中一个事件后,最下方的详细信息窗格会展示该事件的详细信息。没选中事件的时候就展示选定时间范围的综合信息
概览窗格
包括3种图:
FPS
每秒帧数。绿条越高,FPS越高。FPS图上方的红块表示长帧,很可能会卡顿(jank)
CPU
CPU资源。面积图(area chart)表示哪种类型的事件消耗了CPU资源
NET
每个色条代表一个资源,条越长,获取资源需要的时间越长。每个条上浅色的部分表示等待时间(从请求资源到拿到第一个字节的时间)。深色部分表示数据传输的时间(从拿到第一个字节到拿完最后一个字节的时间)
条的颜色含义如下:
HTML:蓝色 Scripts:黄色 CSS:紫色 Media:绿色 其它乱七八糟的资源:灰色
生成记录
打开Timeline面板,打开想要记录的页面,然后reload页面。Timeline面板会自动记录reload过程
要记录页面交互的话,先打开Timeline面板,然后点击记录按钮或者按下快捷键Cmd+E
(Mac)或者Ctrl+E(Windows/Linux)。记录按钮变红表示正在记录。进行页面交互,然后再次点击记录按钮或者按下键盘快捷键停止记录
记录结束时,DevTools会猜测哪部分是你最关心的,并自动把那部分选中
温馨提示:
让记录过程尽可能短。越短越容易分析
避免不必要的动作。避免与想要记录分析部分不相干的外部动作(鼠标点击,网络加载等等)。例如,想要记录发生在点击登录按钮之后的事件,不要同时滚动页面,加载图片等等
禁掉浏览器缓存。记录网络操作时,最好通过DevTools设置面板或者网络情况(Network conditions)选项禁掉浏览器缓存
禁掉插件。Chrome插件会影响应用的Timeline记录,可以在隐身模式(incognito mode)打开Chrome窗口,或者创建一份新的Chrome用户配置(Chrome user profile)来保证环境没有任何插件
查看记录详情
在火焰图中选中一个事件后,详细信息窗格会显示关于事件的额外信息
一些选项卡在所有事件类型中都有,比如Summary,另一些选项卡只在某些事件类型中才有,关于记录类型的详细信息,见Timeline event reference
在记录过程中截屏
Timeline面板能在页面载入过程值截屏,这种特性就像电影胶片一样
在开始记录之前,先在控制窗格勾选Screenshots复选框,就会记录截屏,屏幕截图显示在概览窗格下方
鼠标悬停在屏幕截图,或者概览窗格上,可以查看该时间点放大的屏幕截图。把鼠标从左向右移动可以模拟动画效果
JS性能分析
在开始记录之前,勾选JS Profile复选框可以捕获时间轴记录中的JS调用栈。在开启JS Profile时,火焰图会展示每个被调用的JS函数
绘制性能分析
在开始记录之前,勾选Paint复选框可以深入查看Paint事件。开启绘制性能分析后,点击一个Paint事件时,会在详细信息窗格显示出Paint Profiler选项卡,展示更细粒度的事件信息
渲染设置
打开主DevTools菜单并选择More tools > Rendering settings进行渲染设置,可能有助于调试绘制问题。渲染设置打开之后是一个选项卡,挨在Console选项卡旁边(如果没有的话,按esc显示)
查找记录
在查看事件时,可能只想关注一种类型的事件。例如,可能需要查看每个Parse HTML事件的详细信息
在Timeline面板按下Cmd+F(Mac)或者Ctrl+F(Windows/Linux)打开查找工具条,输入想要查看的事件类型名,例如Event
工具条只对当前选中的时间帧有效,选中时间帧之外的所有事件都不会出现在查找结果中
通过上下箭头可以在结果中按时间顺序移动。所以第一条结果是选定时间段中最早的事件,最后一条是最晚的。每次点击上下箭头时,都会选中一个新的事件,所以可以在详细信息窗格里查看其详细信息。点击上下箭头等价于点击火焰图中的事件
放大一段时间轴
可以把记录的一部分放大,这样更容易分析。可以用概览窗格来放大记录的一部分,缩放之后,火焰图会自动缩放到与这部分对应
想要放大时间轴的一部分的话:
要在概览窗格,用鼠标拖选出时间轴的一部分
调整标尺区域的灰色滑块
选定一部分之后,就可以用WASD
调整选区。W
和S
是放大缩小,A
和D
是左右移动
导出导入记录
可以在概览或者火焰图窗格中通过右键点击来保存和打开,以及选择相关选项
二.动画性能指标
帧率
最直观的是帧率(单位是FPS,表示每秒帧数),30
以下人眼能感受到明显卡顿,60
以上眼睛就看不出来区别了,30-60
基本流畅
性能目标是让动画帧率尽量到达60FPS
,这样就不会有一卡一卡的感觉,所以动画库一般是这样做的:
/* rAF shim. Gist: https://gist.github.com/julianshapiro/9497513 */
var rAFShim = (function() {
var timeLast = 0;
return window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) {
var timeCurrent = (new Date()).getTime(),
timeDelta;
/* Dynamically set delay on a per-tick basis to match 60fps. */
/* Technique by Erik Moller. MIT license: https://gist.github.com/paulirish/1579671 */
timeDelta = Math.max(0, 16 - (timeCurrent - timeLast));
timeLast = timeCurrent + timeDelta;
return setTimeout(function() {
callback(timeCurrent + timeDelta);
}, timeDelta);
};
})();
支持requestAnimationFrame
的话直接用,让浏览器决定执行频率(一般大于60
次每秒),不支持的话降级方案是setTimeout
:
// 16 = 1000 / 60
timeDelta = Math.max(0, 16 - (timeCurrent - timeLast));
要达到每秒60次的话,执行间隔不能超过16ms
,所以setTimeout
延迟时间一般为16 - 函数执行所需时间
,如果函数执行时间超过16ms
,延迟为0
帧率的影响因素很多,比如计算密集的JS任务占用了大片CPU时间导致动画相关JS延后执行、布局变动导致频繁重绘Rendering/Painting
时耗变大、复合层太大太多导致Composite Layers
时耗增大和GPU绘制压力变大时耗变长等等
内存
本来动画与内存没直接关系,但因为硬件加速,就有了动画元素应用硬件加速->创建复合层->消耗内存
的关系
因为CPU与GPU不共享内存,所以创建复合层需要把元素渲染后对应的位图数据打包发送给GPU,这份数据就是“动画消耗的内存”,所以动画卡顿,可能是自己作的,内存也是动画性能指标之一
对应到动画上,就是复合层数量与复合层大小,复合层越多越大,位图数据就越大,消耗内存也越大
三.动画性能调试方法
针对帧率和内存两方面,有一些调试方法:
1.什么东西最耗时间
用Timeline工具记录感觉比较卡顿的一段,选中FPS栏中有红条的部分,看最下方详细信息窗格中的Summary,从饼图找出最耗时的东西,例如:
Range: 294ms - 1.17s
Total: 878.74 ms
29.1 ms Loading
197.0 ms Scripting
43.0 ms Rendering
4.4 ms Painting
116.3 ms Other
489.0 ms Idle
其中JS耗时最多,那么点击Event Log选项卡,按时耗排序,展开前几项,定位到具体JS文件,再分析耗时原因,考虑优化方案
如果是Rendering
耗时最多,那么应该考虑精简HTML结构,去掉不必要的元素,简化布局以及减少reflow;如果是Painting
那么就应该关注复合层,是不是复合层太多太大,考虑去掉多余的、不可见的复合层,以及增大动画元素的z-index
,避免隐式创建复合层
P.S.建议先开启隐身模式,再用Timeline记录,否则各种插件的JS会干扰原因定位(饼图不准确)
2.什么东西最耗内存
观察记录时间段的内存图,选中峰值部分,在Event Log选项卡中找出相关的事件,考虑事件相关的JS
但一般来说JS的影响比复合层影响要小很多,可以直接查看复合层的情况:在概览窗格中拖选出FPS有红条的时间区间,查看详细信息窗格的Layers选项卡,展开document
下的各层,关注几件事情:
复合层的数量是否符合预期,有没有不应该出现的层?
点击层可以查看其大小和内存消耗以及该层的创建原因,看内存消耗是不是太大?
大尺寸的复合层非常耗内存,例如尺寸为375x667
的层消耗内存1.5MB
,如果这种大尺寸的层比较多,会占用大量的内存,内存吃紧肯定会卡顿
而31x31
的小尺寸按钮也需要16KB
,所以除了关注大尺寸动画元素外,复合层的数量也是一个重要因素,比如泡泡,烟雾等需要大量小元素才能实现的动画,考虑效果降级或者减少元素数量,利用伪元素以及border
、outline
、box-shdow
等属性简化复杂的HTML结构
P.S.关于减少复合层,降低内存消耗的更多方法,请查看CSS动画与GPU-性能优化技巧