一个命名引发的性能问题



故事背景

我最近主要在定位、解决当前项目中的一些性能相关问题。

在反馈的问题中,比较严重的问题之一是在户型预览编辑过程,电脑的 CPU 占用率高,及时什么都不做的情况下,CPU 占用也非常的高。

同样的,利用 Chrome 提供的 Performance 录制 ⏺ 了无任何操作的 JavaScript 调用火焰图,发现 Pixi 内部会利用浏览器的requestAnimationFrame接口,执行自身的 render 方法进行 2D 场景的绘制渲染。

202201040901197.jpeg

CPU占用率居高不下

发现问题

初步定位,CPU 占用只可能与 2D 场景中 PIXI 的 render() 有关系,使用 Performance 分析事件调用过程中发现,每次在调用需要使用 9ms 的时间

202201040901243.jpeg

每次Render使用9ms

这个时间是否属于正常的时间范畴呢?

在了解了 PIXI 及绝大部分图形框架后得知,图形框架内部在调用 Render 的过程中其实正常的不会任何过多的计算内容,所有使用到的需要计算生成 Graphics 的地方,都只会生成一遍。

在之后的调用过程中,都会拿到之前生成好的 Buffer 直接进入 Renderer 进行下一帧的渲染,render() 大部分情况下都是在执行脏检查,有任意 Graphics 需要更新时,Graphicsdirty就会被更新,然后重新生成渲染 Buffer

所以,了解了render()方法的作用后,可以确定在静止不动时render()只是执行一些递归判断,不会耗费9ms这么长的时间,这其中定有蹊跷!

在查看到最终调用到的方法部分发现,在 PIXI 内部的render()方法中竟然会调用到 vue 的方法。

202201040901516.jpeg

这样不正常的方法调用让我立刻想起来 vue 中,对data()返回的对象数据做原型链的改写。以及之前看到过的一篇文章:《一个 Vue 引发的性能问题》

之前在看到这篇文章时,只是觉得是一个非常有意思的案例,虽然结局办法并不见得是最优方法。但发现问题的过程非常有价值,原本打算拿到组里来进行分享。没想到报应不爽,这么快就发现我们的项目也存着这一个这样的问题,且影响程度远远大于这篇文章!

所以立马去检查了,2D 与 3D 场景实例化过程中对场景的定义,发现一个并没有对命名做_$的规范处理,这样会导致 scene2D 中的所有对象,都将会被 vue 处理为 vue 的可观察对象,也就是会在原型链中带入 getter/setter。

export default {
 data() {
   return {
     scene2d: null
  };
},
 created() {
   this.initScene2D();
},
 methods: {
   initScene2D() {
     this.scene2d = Scene2D.getInstance();
  }
}
};

解决办法

查看到 vue 的官方文档解释:

202201040901708.jpeg

vue官方解释

所以,为了解决这个问题,需要对data()中不希望 vue 挂载原型链实现数据响应的对象做好命名规范处理。

export default {
 data() {
   return {
     $_scene2d: null
  };
},
 created() {
   this.initScene2D();
},
 methods: {
   initScene2D() {
     this.$_scene2d = Scene2D.getInstance();
  }
}
};

对,就这么简单!

在修改了Scene2D在 Vue 组件的命名后,从整体体验感受来讲“轻快”了许多,再次查看 CPU 及内存占用率都降了许多,render()时间的调用降低到0.94ms,对比如图:

202201040901610.png

Before

202201040904045.png

After

作者:Yee Wang
来源:https://yeee.wang/posts/42c7.html

0 个评论

要回复文章请先登录注册