背景
如果我们将画布理解成一个「工作台」,内容很容易比较多(节点复杂且数量多,且富含交互),就需要探索到我们的画布性能瓶颈是怎样的。也需要充分对比了其它产品的体验效果,希望我们通过 LogicFlow 实现的产品能有类似或更好的体验。
所以我们做了这样的性能测试,尝试探索出 LogicFlow 实现产品的性能瓶颈。
瓶颈的定义
性能瓶颈,用简单的话来说,就是用户感受到页面卡顿时的节点数量或 DOM 数量。
性能瓶颈场景 & 性能评估指标
1. 加载元素渲染时间
通过调研,我们发现当渲染时间超过 500ms 时用户会感受到明显卡顿
所以我们定义一下性能评估的指标:
- 初次渲染瓶颈:初始化渲染 N 个节点时,延迟为 500ms,用户感受明显卡顿
- 增量渲染瓶颈:初始化渲染后,持续增加等量节点(以 100/500/1000 等数量为步长去测量数据),看延迟是否会增大。如果增大,增大的延迟与节点的关系是怎样的
2. 交互时的渲染帧率
根据经验及调研,当页面渲染帧率小于 30fps 时会感受到明显卡顿。
同上,我们也继续定义一下性能评估的指标:
- 移动画布:画布上节点个数为 N 时,帧率降低为30fps。
- 缩放画布:画布上节点个数为 N 时,帧率降低为30fps。
- 拖动元素:画布上节点个数为 N 时,帧率降低为30fps。
技术方案
1. 测量渲染延迟
- console.time('render') 和 console.timeEnd('render'): 只能测量js代码执行时间、不包含dom渲染时间
// 开始计时
console.time('render');
// 执行 DOM 渲染操作
const container = document.getElementById('container');
for (let i = 0; i < 1000; i++) {
const newElement = document.createElement('div');
newElement.textContent = `Item ${i}`;
container.appendChild(newElement);
}
// 结束计时
console.timeEnd('render');
- 使用Performance API:可以测量渲染时间
performance.mark('start');
// 执行 DOM 操作
for (let i = 0; i < number; i++) {
// 添加节点和边的代码
}
requestAnimationFrame(() => {
performance.mark('mid');
requestAnimationFrame(() => {
performance.mark('end');
performance.measure('renderTime', 'start', 'end');
const measure = performance.getEntriesByName('renderTime')[0];
console.log(`Time to render: ${measure.duration} ms`);
performance.clearMarks();
performance.clearMeasures();
});
});
两种方案对比,使用 Performance API 的方案获取的数据更准确一些,所以我们采用 Performance API 方案。
2. 测量帧率
- chrome浏览器自带的FPS工具
优点:准确 缺点:需要人工实时观测,无法收集数据并统计处理数据
- js相关性能api
- requestAnimationFrame:使用 requestAnimationFrame 测量帧率通常依赖于在每个动画帧中执行的代码来记录时间间隔。这可能会受到 JavaScript 执行时间、其他计算任务以及主线程的负担影响,从而导致测量不准确。
new PerformanceObserver().observe({entryTypes: ["frame"]})
目前这个方案还处于处于实验阶段,chrome浏览器还没支持
由于方案二不可行,所以本次采用方案一,保证数据准确性
3. 测量元素
- 不含边的情况下,单个 html 节点 dom 元素约为 9 个
- 含边的情况下,单个 html 节点 dom 元素约为 22 个
- 静态节点,无动画
结果数据
当前数据来源的设备信息:
- 操作系统: macOS 10.15.7
- 浏览器: Chrome 127.0.0.0
- 引擎: AppleWebKit/537.36
注意: 设备、浏览器不同可能会导致测量数据结果有所不同
1. 加载元素
- 初次渲染瓶颈
加载元素 | 只有节点 | 节点 + 边 | ||
---|---|---|---|---|
渲染时间 | 节点个数 | 画布上真实dom的元素个数 | 节点和边个数 | 画布上真实dom的元素个数 |
50ms | 150 | 1479 | 50 个节点 + 50 个边 | 1216 |
100ms | 350 | 3287 | 120 个节点 + 120 个边 | 2756 |
200ms | 800 | 7337 | 280 个节点 + 280 个边 | 6276 |
500ms | 2000 | 18145 | 750 个节点 + 750 个边 | 16616 |
- 增量渲染瓶颈:
2. 移动画布
- 普通 HTML 节点:
流畅度 | 只有节点 | 节点 + 边 | ||
---|---|---|---|---|
帧率 | 节点个数 | 画布上真实dom的元素个数 | 节点和边个数 | 画布上真实dom的元素个数 |
60fps | 5400 | 48729 | 350 个节点 + 350 个边 | 7816 |
50fps | 6200 | 55929 | 400 个节点 + 400 个边 | 8916 |
40fps | 8200 | 73929 | 550 个节点 + 550 个边 | 12216 |
30fps | 12000 | 108129 | 770 个节点 + 770 个边 | 17056 |
20fps | 14500 | 130929 | 1270 个节点 + 1270 个边 | 28056 |
10fps | 23000 | 207129 | 2370 个节点 + 2370 个边 | 52256 |
3. 缩放画布
- 普通 HTML 节点
流畅度 | 只有节点 | 节点 + 边 | ||
---|---|---|---|---|
帧率 | 节点个数 | 画布上真实dom的元素个数 | 节点和边个数 | 画布上真实dom的元素个数 |
60fps | 700 | 6429 | 300 个节点 + 300 个边 | 6716 |
50fps | 900 | 8229 | 400 个节点 + 400 个边 | 8916 |
40fps | 1100 | 10029 | 500 个节点 + 500 个边 | 11116 |
30fps | 1500 | 13650 | 840 个节点 + 840 个边 | 18596 |
20fps | 2100 | 19029 | 1240 个节点 + 1240 个边 | 27396 |
10fps | 2500 | 22633 | 2000 个节点 + 2000 个边 | 44144 |
4. 拖动元素
- 普通 HTML 节点
流畅度 | 只有节点 | 节点 + 边 | ||
---|---|---|---|---|
帧率 | 节点个数 | 画布上真实dom的元素个数 | 节点和边个数 | 画布上真实dom的元素个数 |
60fps | 5100 | 46029 | 400 个节点 + 400 个边 | 8962 |
50fps | 5800 | 52329 | 500 个节点 + 500 个边 | 11162 |
40fps | 6600 | 59529 | 620 个节点 + 620 个边 | 13802 |
30fps | 7100 | 64029 | 840 个节点 + 840 个边 | 18642 |
20fps | 9100 | 82050 | 1240 个节点 + 1240 个边 | 27442 |
10fps | 11100 | 100100 | 2500 个节点 + 2500 个边 | 55159 |
结论
1. 性能瓶颈结论
-
加载元素:
- 初次渲染瓶颈:
- 初次渲染 2000 个节点,DOM 元素数量约为 1600,延迟为 500 毫秒 ,明显卡顿。
- 初次渲染 750 个节点&边,DOM 元素数量约为 1800,延迟为 500 毫秒 ,明显卡顿。
- 增量渲染瓶颈:在初始化渲染后,持续增加等量节点的情况下,渲染时间上下波动,总体上呈现出逐渐增加的趋势。
-
交互帧率:
- 移动画布:
- 画布上有 12000 个节点,DOM 元素数量约为 108129, 帧率降低为 30 帧。
- 画布上有 750 个节点&边,DOM 元素数量约为 17056, 帧率降低为 30 帧。
- 缩放画布:
- 画布上有 1500 个节点,DOM 元素数量约为 13650, 帧率降低为 30 帧。
- 画布上有 840 个节点&边,DOM 元素数量约为 18596, 帧率降低为 30 帧。
- 拖动元素:
- 画布上有 7100 个节点,DOM 元素数量约为 64029, 帧率降低为 30 帧。
- 画布上有 840 个节点&边,DOM 元素数量约为 18642, 帧率降低为 30 帧。
- 移动画布:
小插曲
在测试的过程中,我们发现交互的时候会出现下图所示的元素闪烁的问题:
出现原因: 页面闪烁是动画数量太多,超过GPU内存,是浏览器性能问题,与 LogicFlow 性能无关。
浏览器 GPU:浏览器的GPU用于加速图形渲染和处理计算密集型任务。它负责处理页面中的图像、动画和视频,提升渲染性能,减少CPU负担,确保流畅的用户体验。GPU的内存大小并不是一个固定的数值,跟电脑操作系统、显卡硬件有关。
Demo 参考项目
最后奉上我们的项目 LogicFlow 的 GitHub 地址 - github.com/didi/LogicF…,欢迎使用、关注 Star 并与我们交流,我们有用户群解决用户的各种问题