性能的极限:深挖 LogicFlow 瓶颈的技术之旅

991 阅读7分钟

背景

如果我们将画布理解成一个「工作台」,内容很容易比较多(节点复杂且数量多,且富含交互),就需要探索到我们的画布性能瓶颈是怎样的。也需要充分对比了其它产品的体验效果,希望我们通过 LogicFlow 实现的产品能有类似或更好的体验。

所以我们做了这样的性能测试,尝试探索出 LogicFlow 实现产品的性能瓶颈。

瓶颈的定义

性能瓶颈,用简单的话来说,就是用户感受到页面卡顿时的节点数量或 DOM 数量。

性能瓶颈场景 & 性能评估指标

1. 加载元素渲染时间

通过调研,我们发现当渲染时间超过 500ms 时用户会感受到明显卡顿

image.png

所以我们定义一下性能评估的指标

  • 初次渲染瓶颈:初始化渲染 N 个节点时,延迟为 500ms,用户感受明显卡顿
  • 增量渲染瓶颈:初始化渲染后,持续增加等量节点(以 100/500/1000 等数量为步长去测量数据),看延迟是否会增大。如果增大,增大的延迟与节点的关系是怎样的

2. 交互时的渲染帧率

根据经验及调研,当页面渲染帧率小于 30fps 时会感受到明显卡顿。

image.png

同上,我们也继续定义一下性能评估的指标

  • 移动画布:画布上节点个数为 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工具

chrome fps.png

优点:准确 缺点:需要人工实时观测,无法收集数据并统计处理数据

  • js相关性能api
    • requestAnimationFrame:使用 requestAnimationFrame 测量帧率通常依赖于在每个动画帧中执行的代码来记录时间间隔。这可能会受到 JavaScript 执行时间、其他计算任务以及主线程的负担影响,从而导致测量不准确。
    • new PerformanceObserver().observe({entryTypes: ["frame"]}) 目前这个方案还处于处于实验阶段,chrome浏览器还没支持

由于方案二不可行,所以本次采用方案一,保证数据准确性

3. 测量元素

  • 不含边的情况下,单个 html 节点 dom 元素约为 9 个
  • 含边的情况下,单个 html 节点 dom 元素约为 22 个
  • 静态节点,无动画

image.png

结果数据

当前数据来源的设备信息:

  • 操作系统: macOS 10.15.7
  • 浏览器: Chrome 127.0.0.0
  • 引擎: AppleWebKit/537.36

注意: 设备、浏览器不同可能会导致测量数据结果有所不同

1. 加载元素

  • 初次渲染瓶颈
加载元素只有节点节点 + 边
渲染时间节点个数画布上真实dom的元素个数节点和边个数画布上真实dom的元素个数
50ms150147950 个节点 + 50 个边1216
100ms3503287120 个节点 + 120 个边2756
200ms8007337280 个节点 + 280 个边6276
500ms200018145750 个节点 + 750 个边16616
  • 增量渲染瓶颈:
image (5).png image (6).png image (7).png image (8).png image (9).png

2. 移动画布

  • 普通 HTML 节点:
流畅度只有节点节点 + 边
帧率节点个数画布上真实dom的元素个数节点和边个数画布上真实dom的元素个数
60fps540048729350 个节点 + 350 个边7816
50fps620055929400 个节点 + 400 个边8916
40fps820073929550 个节点 + 550 个边12216
30fps12000108129770 个节点 + 770 个边17056
20fps145001309291270 个节点 + 1270 个边28056
10fps230002071292370 个节点 + 2370 个边52256

3. 缩放画布

  • 普通 HTML 节点
流畅度只有节点节点 + 边
帧率节点个数画布上真实dom的元素个数节点和边个数画布上真实dom的元素个数
60fps7006429300 个节点 + 300 个边6716
50fps9008229400 个节点 + 400 个边8916
40fps110010029500 个节点 + 500 个边11116
30fps150013650840 个节点 + 840 个边18596
20fps2100190291240 个节点 + 1240 个边27396
10fps2500226332000 个节点 + 2000 个边44144

4. 拖动元素

  • 普通 HTML 节点
流畅度只有节点节点 + 边
帧率节点个数画布上真实dom的元素个数节点和边个数画布上真实dom的元素个数
60fps510046029400 个节点 + 400 个边8962
50fps580052329500 个节点 + 500 个边11162
40fps660059529620 个节点 + 620 个边13802
30fps710064029840 个节点 + 840 个边18642
20fps9100820501240 个节点 + 1240 个边27442
10fps111001001002500 个节点 + 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 帧。

小插曲

 在测试的过程中,我们发现交互的时候会出现下图所示的元素闪烁的问题: 录屏2024-08-07-02.45.34.gif

出现原因: 页面闪烁是动画数量太多,超过GPU内存,是浏览器性能问题,与 LogicFlow 性能无关。

浏览器 GPU:浏览器的GPU用于加速图形渲染和处理计算密集型任务。它负责处理页面中的图像、动画和视频,提升渲染性能,减少CPU负担,确保流畅的用户体验。GPU的内存大小并不是一个固定的数值,跟电脑操作系统、显卡硬件有关。

Demo 参考项目

最后奉上我们的项目 LogicFlow 的 GitHub 地址 - github.com/didi/LogicF…,欢迎使用、关注 Star 并与我们交流,我们有用户群解决用户的各种问题