一个命名引发的性能问题
故事背景
我最近主要在定位、解决当前项目中的一些性能相关问题。
在反馈的问题中,比较严重的问题之一是在户型预览编辑过程,电脑的 CPU 占用率高,及时什么都不做的情况下,CPU 占用也非常的高。
同样的,利用 Chrome 提供的 Performance 录制 ⏺ 了无任何操作的 JavaScript 调用火焰图,发现 Pixi 内部会利用浏览器的requestAnimationFrame
接口,执行自身的 render
方法进行 2D 场景的绘制渲染。
发现问题
初步定位,CPU 占用只可能与 2D 场景中 PIXI 的 render()
有关系,使用 Performance 分析事件调用过程中发现,每次在调用需要使用 9ms
的时间
这个时间是否属于正常的时间范畴呢?
在了解了 PIXI 及绝大部分图形框架后得知,图形框架内部在调用 Render 的过程中其实正常的不会任何过多的计算内容,所有使用到的需要计算生成 Graphics 的地方,都只会生成一遍。
在之后的调用过程中,都会拿到之前生成好的 Buffer
直接进入 Renderer
进行下一帧的渲染,render()
大部分情况下都是在执行脏检查,有任意 Graphics
需要更新时,Graphics
的dirty
就会被更新,然后重新生成渲染 Buffer
。
所以,了解了render()
方法的作用后,可以确定在静止不动时render()
只是执行一些递归判断,不会耗费9ms
这么长的时间,这其中定有蹊跷!
在查看到最终调用到的方法部分发现,在 PIXI 内部的render()
方法中竟然会调用到 vue 的方法。
这样不正常的方法调用让我立刻想起来 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 的官方文档解释:
所以,为了解决这个问题,需要对data()
中不希望 vue 挂载原型链实现数据响应的对象做好命名规范处理。
export default {
data() {
return {
$_scene2d: null
};
},
created() {
this.initScene2D();
},
methods: {
initScene2D() {
this.$_scene2d = Scene2D.getInstance();
}
}
};
对,就这么简单!
在修改了Scene2D
在 Vue 组件的命名后,从整体体验感受来讲“轻快”了许多,再次查看 CPU 及内存占用率都降了许多,render()
时间的调用降低到0.94ms
,对比如图:
作者:Yee Wang
来源:https://yeee.wang/posts/42c7.html