当升级到2.5.x后,我们发现官方的功能仍然不太完善,还存在不少问题,例如,崩溃、黑/白屏、内存泄漏等。另外,的性能也被社区吐槽得厉害,特别是端。
于是,在今年1月初的时候我们成立了一个优化小专项,希望对的已知问题进行一轮集中处理,并探索性能优化方案,快速解决业务痛点。
两个月过去了,优化专项已处于收尾阶段,工作基本告一段落。这里向大家同步一下工作进展和阶段成果,以及后续的规划。
阶段成果
立项时明确了两个目标:一是集中解决的已知问题;二是探索的性能优化方案。
已知问题处理
我们发现并解决了十多个相关的体验问题,涉及各个场景下的黑/白屏、资源泄漏、崩溃,以及死锁等。到目前为止,已知问题已基本扫清。
在此过程中,我们也积极与社区进行沟通和交流,将我们的一些修复贡献给官方。以下是与相关的PR和issue:
PR
Fix with on Q()
Using ent for API lower than 26()
Minor : move to from ler(官方打算弃用,觉得没有必要合并该优化)
the from to avoid leaks()
Check weak to avoid use-after-free crash()
Add to avoid by (官方觉得全局锁的方案不太优雅,晚点再重新搞个方案)
[] Fix black and blank on the view page()
[] Add for .(其实,我只是想修复关闭合并线程开关的一些代码问题)
issue
[][] Views are not on Q
[ , , ] white
[][][ ] leak in
[IOS] -views by
[ ][] App when two view pages one after in mode
[ ][] The page a view using gets a black when going back from
[ ][] The view using is when it is for the time.
性能优化探索性能问题
目前,上官方提供了两种实现方案: 和 。
方案需要先将 View绘制到虚显,然后通过从虚显输出中获取纹理并将其与自己内部的树进行合成,最后作为在 上更大的纹理输出的一部分进行渲染。该方案除了存在性能问题外,更重要的是有很多难以解决的功能性问题,例如,事件/文本输入/可访问性。为了解决这些问题,官方推出了 ,并寄予众望。
是通过获取 UI的输出,并将其转为,然后与 View进行合成。该方案的确能够解决 方案中的一些缺陷,特别是可访问性和键盘相关的问题。
但是, 自身也存在一些性能和稳定性问题:一方面,线程与线程合并带来了潜在的性能损失,以及死锁风险(#/94524)。另一方面,在将 UI转为的过程中,由于 Q以下的版本不支持直接通过创建,需要执行函数进行每一帧的内存拷贝,所以在某些场景下性能非常差,这也是社区集中吐槽的点。
下图是社区一哥们抓的火焰图,借来用用。从图中我们能清楚地看到拷贝内存操作的耗时。
挖洞模式
为了解决上述性能问题,在早些时候实现了挖洞模式,其核心原理归纳起来有两点:
将 View放到下方,并将中对应于 View的位置透明化处理(就像是挖了个洞), View与的渲染互不干扰,但从用户视觉上看是一个整体
在 中计算 View位置、大小、透明度等等的信息,并在 中的合适时机根据 中的计算结果来更新 View,以此做到线程拆分,并做到了 View与其他元素的显示同步,不会出现滚动黑边等问题。
挖洞模式确实有很好的性能,但是它自身的局限性也很明显,不支持半透明,矩阵变换,蒙版,各种过滤器特效等。所以,针对挖洞模式不能覆盖的场景,我们还需要继续探索~~
优化思路
方案已逐渐被官方废弃,不需要考虑,我们开始了两个方向的尝试:
对 方案进一步挖掘,例如,不合并线程,避免不必要的转换;
寻求全新的解决方案——移植Roger大神在我们U4浏览器内核实现的「Embed 方案」;
尝试不合并线程
前面分析了, 的性能问题来源于线程合并和内存拷贝,所以我们快速尝试了不合并线程和不转换类型的(这里的同步问题暂未处理),以期望能对快速地对性能摸个底,看看优化空间是否足够大,值不值得继续投入优化。
移植Embed
这里有个小插曲,放完春节回来,我们在评估移植U4的「Embed 方案」过程中,发现官方的同学也恰巧在做类似的事情(#pull/31198),并且代码已完成的差不多了。于是决定与他一起完善,这很大程度上节省了我们移植代码的时间。
代码移植完后,一轮测试下来,除了发现一个 Q上显示白屏(#/98722)外,其他表现良好,页面切换也不存在 的闪黑/白问题,比预期来得顺利。
关于 Q的显示问题,我们给官方提出了修复方案(#pull/31698),得到了同学的认可,并快速合入了主线。另外一个优化点(#pull/31533),也很快被合入了主线。
测试数据
除了官方支持的和外,还提供了挖洞模式(),再加上和不合并线程的listview的优化,目前有五种方案。针对这几种方案我们设计了对比测试案例。
场景一:滑动
在该测试案例(源码:)中有个, UI和作为item,交替出现。测试时,滑动该列表,效果如下面视频所示:
第一组数据
表1(, 9)
表2(, 10)
小结:
与挖洞模式帧率接近,性能最好,大约60帧;
在 9上,大约50帧,比低了10帧左右;
不合并线程的的表现也不如方案好;
该场景性能比较差,不到30帧;
第二组数据
表3(Mi 8, 9)
表4(, 11)
小结:
挖洞模式性能最好,接近60帧;
在 9上,大约50帧,比低了6、7帧左右;
不合并线程的的波动比较大;
该场景性能比较差,保持在40帧以下;
场景二:复杂动画
如视频所示,在该场景(源码:)中,中间的「蓝色大嘴巴」是动画,最上面「跳动的小球」是 View实现的,而下边「游动的鱼」是嵌入了一个。
第一组数据
表5 (, 9)
表6(, 10)
小结:
//挖洞帧率接近,大约60帧,几乎无抖动;
:在上的性能比较糟糕,只有30帧,抖动非常厉害;
上基本可以到60帧,但是也存在抖动;
不合并线程的在上也可以达到56帧,但抖动也比较厉害;
第二组数据
表7(Mi 6, 9)
表8(, 11)
小结:
//挖洞帧率接近,大约60帧,有轻微抖动;
在上,帧率只有25,出现严重抖动和卡顿;
不合并线程的在上接近59帧,存在抖动和轻微卡顿;
测试结论
通过了解实现原理和上面的测试数据,我们可以得出如下结论:
「挖洞模式」的性能仍然是最好的。在挖洞模式能满足的场景下,请考虑优先使用挖洞;
Embed 除了仅支持.0及以上外,没有明显短板,其帧率在大多数场景下可以与挖洞模式持平;
从测试数据看,不合并线程的的帧率波动比较大,并不比Embed 有优势。另外,因为不能完全避免以下的内存拷贝,同时还需要处理部分场景下的同步问题,所以我们不会继续优化该方案。
另外,官方上了Embed 方案后,会直接删除实现,估计也会逐渐弃用 ,上不再需要合并线程了。
后续工作
因目前端的的问题比较突出,所以该轮优化主要集中在端,后续除了持续优化端外listview的优化,也会更多地关注iOS端,为大家提供更好的开发体验,满足更多的业务场景。
写在最后
UC 内核团队,专注渲染引擎 & 虚拟机技术十数年。作为阿里巴巴集团经济体共建 的重要参与方,积极拥抱社区,力求给业务带来最大化的价值提升。 是我们深度定制优化的 引擎,融合了团队在 Web 引擎上的多年技术沉淀。欢迎从事相关技术研究或基于 构建应用的同学提出宝贵的意见或建议。
U4内核致力于打造性能最好、最安全的web平台,让web无所不能;
引擎旨在全面对标性能,提升业务开发效率及体验。
限时特惠:本站持续每日更新海量各大内部创业课程,一年会员仅需要98元,全站资源免费下载
点击查看详情
站长微信:Jiucxh