1. 性能瓶颈分析

以下为 CPU 性能限定为 4x slowdown 情况下录制。

image-20221214172254940

切页时,会调用两个函数 initMenuTree (耗时 1.12 s)和 refreshWnd (耗时 84.2 ms)。

其中,initMenuTree 函数分成 3 个部分,第一部分(匿名函数)用来向树中插入节点,耗时 87.7 ms;第二部分展开所有节点,耗时 888.5 ms;第三部分选中需要的节点,耗时 142.5 ms。

通过分析可知,有以下几方面可以考虑优化:

image-20221214173215049

  1. 在第二部分 e.expandAll 中,花费大量时间将所有节点展开,然后紧接着在 onClickTreeNode 中调用 e.collapseAll 又将所有节点折叠。需要评估一下,展开所有节点的目的是什么,是否可以删除。
  2. onClickTreeNode 中调用了 refreshWnd 函数,然后 initMenuTree 结束后再次调用了一次 refreshWnd 函数,同一个操作中,可能不需要重复调用。

image-20221214174402789

  1. refreshWnd 函数(耗时 84.2 ms)中,有 73% 的时间被 refreshReddot 函数占用(耗时 61.6 ms)。可以尝试优化小红点的判断逻辑,或者延迟判断。

2. 优化方法

总体思路是,没必要的函数不调用,不需要立即执行的函数延迟调用,耗时长的函数拆分后分帧执行。

2.1 删除 expandAll 函数

分析发现,e.expandAll() 函数耗时特别长,且树的节点越多耗时越长。

经测试,这个函数并非必须的,删除以后对功能几乎没有影响,但是对性能的提升非常明显。

2.2 refreshWnd 函数的调用次数

在代码中,refreshWnd() 函数调用的位置有以下几处

  1. 界面初始化结束后,onInit()
  2. 收到刷新界面消息时,Msg()
  3. 切换子页签时,setPageIndex()
  4. 切换选中的树节点时,onClickTreeNode()

这几处调用都是合理且有必要的。

但是通过性能分析工具可知,在 Msg()setPageIndex() 中都会调用 initMenuTree() 来初始化树状列表,而 initMenuTree() 中又会调用 onClickTreeNode() 来选中一个默认的节点。这样就会导致每次切换页签或者其它操作都会重复调用 refreshWnd() 函数。

解决方法

initMenuTree() 不再调用 onClickTreeNode() ,选中默认节点的功能改用其它方式实现。

// 通过 selectNode() 函数实现选中节点
this.itemTree.selectNode(defaultNode);

这样保证了每次切页等操作,只会调用一次 refreshWnd() 函数,节省了不必要的界面刷新。

2.3 refreshReddot小红点刷新优化

目前每次刷新界面,都会调用一次小红点刷新。但是小红点状态判断需要的计算量很大,耗时比较长。

这部分优化有三个方向的思路,

  1. 优化小红点状态判断算法,减少计算量;
  2. 对小红点刷新时机做更精细化的判断,减少函数调用;
  3. 将小红点刷新操作进行分帧处理。

第一个,优化空间不大,因为该系统本身就是需要判断所有物品的小红点状态,而且每个物品的合成条件由配置决定,没有固定的规律,所以优化空间不大。

第二个,经测试,在每次刷新界面时,都必须相应地刷新小红点,任何一处没刷,都会导致小红点显示异常。所以这里也没有办法。

第三个,分帧处理,将一些耗时的操作分帧处理,来缓解每一帧的运算压力,是一种几乎通用而且好用的优化手段。

经过分帧处理后,将耗时 60ms 左右的小红点刷新函数剥离出去,原来的 refreshWnd() 函数执行时间降低到 30 ms 左右。

3. 优化效果

通过优化,合成系统的切页操作耗时,由原来的 1200 ms 左右降低到 103 ms 左右,节省了 91.5% 左右的时间。

最后修改:2022 年 12 月 16 日 10 : 15 AM
如果觉得我的文章对你有用,请随意赞赏