前言

前段时间实现了一个使用 opencvjs 将印花图与 mask 图原图进行正片叠底的效果,印花使用四方连续的形式拼接,大概效果如下图所示

Untitled

测试反馈说生成图时操作会变得卡顿,于是便开始着手优化了。

找到问题

打开 performance 录下操作,发现有长任务需要优化。

Untitled

可以发现有一个比较长的任务,我们看看调用栈分析一下哪个函数时间比较长。从图上可以发现整个 task 主要耗时的函数有 getSourceMatAndMaskMat 和 mergeImage 两个。查看一下 render 函数

Untitled

拆分 long task

对于主线程中的长任务可以使用在  Promise  中调用  setTimeout 的方式让出主线程。

1
2
3
4
5
export function yieldToMain() {
return new Promise((resolve) => {
setTimeout(resolve, 0);
});
}

在两个函数之间加上 yieldToMain

Untitled

再录一次操作发现顺利拆分了

Untitled

那么继续看看 getSourceMatAndMaskMat 这个函数里面哪些需要拆出来的

拆分 for 循环中的 long task

Untitled

可以看到 getSourceMatAntMaskMat 主要耗时是在 loopRect 这个循环中,看看 loopRect 做了什么

Untitled

主要是做了遍历区域像素的操作,就算是 100x100 的遍历也要遍历 10000 次,我们可以试一下将长循环拆分成多个批次来进行,接下来我们试一下改造 loopRect 函数来拆分一下。

Untitled

看看效果

Untitled

可以看出,long task 被成功拆分了,从原先一整个 task 超过200ms的任务,变成了一个个任务块,最大的任务块也就 40ms 左右,也就是在生成图的过程中是不会影响用户其他操作的。

一些补充

尽量不要使用匿名函数

在函数编写中,尽量不要使用匿名函数来创建函数,因为这会使你在查找函数栈时变得复杂

Untitled

从图上很难直观得看出这是个什么函数,到底做了什么。

函数封装

每一个功能应该拆分出一个函数出来,每个函数要尽量只做一个事情,拆分成函数才能更好地拆分 task。如何拆分函数可以看看代码整洁之道的第三章

结语

合理利用 chrome performance 板块,我们可以将一个个 long task 给切分成一个个 short task,减少我们因为长任务导致的操作卡顿。

参考