腾讯文档渲染优化之路
如果无法正常显示,请先停止浏览器的去广告插件。
1. 腾讯文档渲染优化之路
肖骏 腾讯AlloyTeam 前端高级工程师
2. 个人介绍
肖骏,腾讯前端高级工程师,目前在AlloyTeam专注
腾讯文档doc排版渲染开发和优化,致力于跟团队一起
打造在线文档精品。
腾讯AlloyTeam
肖骏
3. 目录
1. 腾讯文档 ,科技向善
2. 腾讯文档渲染优化之路
3. AlloyPerf精确统计FPS
4. 01
腾讯文档 ,科技向善
5. 腾讯文档 — 可多人
协作的在线文档
· 1.6亿+
月活用户数超过1亿6千万
· 1600万+
日活用户数峰值超过1600万
· 300万+
日活跃文档数超过300万
腾讯文档 — 可多人协作的在线文档
6. 科技向善,一直在路上
《环球网》报道
《中国青年报》报道 《央视新闻》报道
《环球网》对河南暴雨“救命文档”的报道,3000多人 《中国青年报》对河南暴雨的报道,报道了“救 《央视新闻》对河南暴雨“救命文档”的报道,
通过“救命文档”得到救助 命文档”的创建过程。 报道了文档的的实时记录过程
7. 保证极限场景的渲染交互
河南暴雨“救命文档”
河南暴雨救灾过程中,应对极限情况,腾讯文档需要支撑数十万行的数据渲染交互不卡顿
8. 02
腾讯文档渲染优化之路
在渲染上如何一步步优化?
9. 腾讯文档渲染优化之路
青铜时代 — Dom
腾讯文档最早期,使用Dom渲染
白银时代 — Canvas
腾讯文档中期,升级为Canvas渲染引擎
黄金时代 — 进阶优化
现在,基于Canvas渲染引擎进一步优化
10. 青铜时代的Dom渲染
Sheet — Handsontable Doc — React
初始阶段,Sheet使用Handsontable作为dom渲染的UI框架 初始阶段,Doc使用React作为dom渲染的UI框架
11. Sheet页面构成
腾讯文档Sheet页面示例
· 单元格
数量多、结构类似
· 图表、选区
数量少、结构独立
单元格
图表
选区
12. 渲染管道优化
Layout优化
Paint优化
• 尽量避免改变元素的几何属性(例如宽度、高度、左侧或顶部位置等) • 使用 will-change 或 translateZ 等提升元素层级
• 修改“paint only”属性(例如背景颜色、文字颜色等)-> repaint • 使用 transform 和 opacity 属性
13. Dom滚动复用
通过Dom复用极大减少了渲染的Dom数量
14. Recalculate Style / Layout开销
Layout
Recalculate Style
开销
开销
元素复杂+滚动速度
元素复杂+滚动速度
随着页面元素复杂程度和页面滚动速度的增加,Layout 开销随之线性增长
随着页面元素复杂程度和页面滚动速度的增加,Recalulate Style 开销随之极大增加
15. 升级Canvas渲染引擎
· 业务逻辑
· Recalculate Style
· Layout
Canvas渲染层级
16. Canvas渲染模型
· 背景色
· 边框线
· 文字
以Sheet为例,Canvas渲染模型可抽象为每个单元格背景色、边框线、文字的渲染
17. 极端场景全表双边框
· 绘制量 X 2
相比于单边框,全表双边框绘制量直接翻倍
· 对接逻辑
对于双边框的绘制需要额外的对接逻辑。
渲染时间:单边框6.71ms -> 双边框 14.91ms
18. 业务逻辑都在干什么?
内存GC 调用Canvas API
浏览器垃圾收集器会定期(周期性)找出那些不在继续使用的 设置状态:strokeStyle、fillStyle
变量,然后释放其内存 执行绘制:fillText、strokeText、drawImage
19. 减少渲染时触发GC
频繁GC导致帧率不稳定
render
• 大量创建对象会导致更频繁的GC
• 频繁的GC会导致帧率不稳定,引起卡顿
new obj()
GC
init static pool
render
get obj from pool
对象池优化
• 建立对象池缓存,从对象池获取对象
• 减少渲染主循环过程中新建对象操作,从而减少GC
20. Canvas API调用的分析
CanvasStyle类继承关系
· 上下文设置style
通过canvas上下文设置的fill_style、stroke_style等状
态, 实际上都是CanvasStyle这个类的实例
· GC逻辑低效
GarbageCollected 自己实现了一套GC逻辑,并不会
走V8引擎的GC,不如C++ GC高效
· 性能开销
Canvas API调用设置状态操作,性能开销较大
CanvasStyle 类继承于GarbageCollected类,GarbageCollected 自己实现了一套GC逻辑,并不会走V8引擎
的GC,不如C++ GC高效
21. Canvas 切换状态机
· 渲染单元格
渲染背景色
渲染边框线
渲染富文本
……
· 渲染文本
渲染color: black文字
渲染color: red文字
渲染color: blue文字
……
Canvas在渲染过程中针对单元格需要渲染背景色、边框线、富文本,而不同的颜色、文本等又
需要不同的状态,所以需要频繁切换状态机
22. Canvas 切换状态机优化
· 遍历待绘制内容
遍历待绘制的所有边框线、文字等
· 相同状态内容整理
相同状态的绘制内容,进行合并、排序等整理
· 分类渲染
整理之后的内容,根据状态机进行分类渲染
通过提前收集、统一整理,减少切换状态机
23. Canvas 渲染复用
页面滚动时canvas渲染复用
· 离屏Canvas
离屏Canvas缓存主Canvas绘制内容
· 复用
直接复用离屏Canvas缓存的内容
通过离屏Canvas的复用,尽量减少canvas重绘
24. 03
AlloyPerf精确统计FPS
如何精确、自动化统计FPS?
25. 现有Web前端FPS统计方式
类似Chrome devtools的开发者工具
• 需要人工实时监测、无法自动化;
• 更加适合开发阶段进行自测;
RequestAnimationFrame API
• 统计的FPS结果不够准确,因为以两次主线程执行时间间隔作为一帧;
• 模拟交互不够真实,需要引入脚本且实现对应代码;
26. 主线程阻塞情况下浏览
器的优化
主线程阻塞情况下页面的滚动
· 嵌入死循环js
<script>
function block() {
while (true) {}
}
setTimeout(block, 2000);
</script>
· 滚动依然流畅
页面嵌入死循环js,导致主线程完成阻塞,然而页面滚动依然十分流畅
27. Chrome浏览器做了什么?
主线程滚动
合成线程滚动
Chrome浏览器在主线程阻塞情况下,会将页面滚动处理由主线程移交给合成线程
28. FPS本质是什么?
渲染管道
· 帧率
浏览器渲染动画或页面每一帧的速率
· 渲染管道
浏览器渲染每一帧经历的固定流程
主线程
合成线程
GPU线程
主线程阻塞情况下,页面渲染可能在其之后的合成线程、GPU线程进行处理
29. Tracing工具
Chrome devtools performance
普遍常用的Chrome devtools performance面板
Tracing view
信息更加详细、全面的的Tracing view工具,使用chrome://tracing开启
30. Tracing view介绍
· 进程、线程
展示对应进程、线程信息
· Flow
事件对应的流向
· TRACE_EVENT
表示浏览器内核函数调用执行情况
Tracing view
31. 分析主线程阻塞时滚动
Tracing view示例图
· 主线程V8执行js死循环
· 合成线程处理滚动
合成线程
处理滚动
32. 寻找关键TRACE_EVENT
帧数据渲染流向
· 关键TRACE_EVENT
关键TRACE_EVENT出现次数 = 帧渲染次数
· 排除主线程
将寻找关键TRACE_EVENT的路径集中到主线程之后
的流程
寻找关键TRACE_EVENT将主线程排除,关注点放在后面的合成线程
33. 确定关键TRACE_EVENT
· 合成线程中TRACE_EVENT
ProxyImpl::ScheduledActionDraw
确定关键TRACE_EVENT,合成线程中 ProxyImpl::ScheduledActionDraw 可以作为
关键TRACE_EVENT进行FPS统计
34. AlloyPerf 实现原理
Action
Emulation
静态
滚动
自定义
移动端
CPU
网速
清洗过滤
详细分析FPS异常原因
35. 跨平台环境、自动化CI
· Docker集成环境
docker集成chrome、chrome driver统一环境,支持
跨平台
· 自动化CI
支持自动化CI,通过测试用例自动化测试统计对应、
对应交互的页面FPS
Docker 集成环境
自动化CI流水线
36. 高效复用、快速接入
· npm包
公司内部npm打包发布,集成npm快速接入
· 全面开源
AlloyPerf 在公司内部打磨完善后,未来会对外全面开
源,to be continued……
内部npm包
37. 感谢倾听
大会官网