浏览内核CPU不收敛问题定位和分析

一 、问题发现

        浏览内核CPU 占用问题是影响用户使用的关键因素,会引起操作卡顿、手机耗电以及应用崩溃等问题。CPU 占用由于受到页面、机型、应用框架的影响,较难进行定位和优化。在浏览内核性能测试中发现CPU 占用高于之前版本,通过分析CPU 曲线,发现可能是部分场景CPU 没有收敛导致的,因此对CPU 收敛做了专项测试。

 

老测试方案——内核之前针对CPU 收敛的测试是通过加载用户访问Top 100 的页面,应用放后台通过TOP 命令观察CPU 收敛情况。

缺点: 1.测试工作量非常大;2.Top页面结构复杂很难分析是页面中的那个元素引起的不收敛现象;3.页面经常变化,收敛状态难以稳定复现;

 

新测试方案——通过对页面的整理分析和与RD 的沟通,引起CPU 不收敛的问题主要是页面中的动态元素,如CSS动画、 视频播放、 Timer 定时器、 js 加载、 gif动图等,并由QA自己开发动态元素的测试页面进行测试,在测试场景上选择了应用切后台和页面切换至不可见窗口两种。

优点:1.测试工作量小,针对性强;2.页面固定、元素简单,易于复现;3. 场景更加贴近用户使用;

测试结果:

使用新测试方案完成测试后,发现如下几个问题:

   问题一   页面切换至不可见窗口场景:测试中心、CSSH5GIF 等多个页面不收敛;

   问题二   APP 切后台场景:测试中心页面不收敛;

 

二 、初步分析

        问题一通过对测试结果的分析,当用户创建空白页面,使测试页面切换至不可见窗口的场景时,包含GIF 图、CSS 动态元素的页面CPU 仍然有波动,而没有收敛至0 这里可以猜测出可能当页面不可见时,页面的绘制没有进行pause,内核判断页面动态元素有更新,因此还在不断的进行绘制,导致CPU 不为0

 

        问题二测试中心页面不收敛,该页面是测试平台的导航页面,本身没有动态元素,但是仍有不收敛现象。QA通过绘制工具systrace分析页面加载过程的数据,发现切换切换后台后,内核的main(scheduler)/thread_proxy(cc)在工作,还在绘制内容。推测有两个可能:1.APP切后台,没有pause当前的webview2.测试中心首页的某些逻辑导致内核没被pause


三 、业务和代码分析

        内核加载过程——浏览内核加载页面的网络部分向网络发起请求并把网页资源下载给Loader,之后 HTML Parser HTML Script 解析成 DOM 树,再结合 CSS 层叠样式表生成对应的 Render 树。Render 树上就包含了每个元素的各种属性字体、颜色、屏幕上面的坐标、长、宽等。Render 树进一步生成 Graphics Context 交给平台相关的图形库把页面展示在屏幕上。大体过程是如下图这样,实际上内核是边加载边绘制的。


              

 

 

 

 

 

 

 

 

 

 

        内核绘制过程——浏览内核为了减少不必要的绘制,保证页面的绘制速度和页面滑动的流畅度,对Render Tree 进行了分层,可以更方便的进行页面的局部更新。有更新的layer 将更新数据经过合成composite阶段,将真正的需要绘制区域数据给GL 进行draw,最终完成图像的显示。整个过程是多线程的,通过消息来驱动这个流程。

 

        动态元素由于更新频率高,通常会划分为单独的layer,如果这个layer 的更新消息没有暂停,就会导致这个消息驱动layer 的合成,进行更新区域的计算,调用drawGL 进行图像的draw。因此CPU 会一直被占用。

 

四、真相大白

        问题一:页面动态元素不收敛,通过将页面中动态元素删除后,测试结果为收敛,由此可以证明确实是动态元素导致。动态元素有频繁的更新消息,使得不断对包含动态元素的layer 进行合成和绘制,导致CPU 不收敛。首先需要查看该webview 是否是否被pausewebview 被切换至不可见状态时,策略上应该暂停一切页面的处理,节约更多的CPU和内存资源保证当前活动窗口的处理。

 //  webview onPause 的处理

public void onPause() {

        if (mIsPaused || mNativeAwContents == 0) return;

        mIsPaused = true;

        nativeSetIsPaused(mNativeAwContents, mIsPaused);

}

 内核没有对文档解析、js 加载、动图进行暂停。

 

解决方案:

// 网络加载暂停

void ContentViewCoreImpl::SetIsPaused(JNIEnv* env,

                                      jobject obj,

                                      bool paused) {

  GetWebContents()->Send(new ViewMsg_SetIsPaused(

      GetWebContents()->GetRoutingID(), paused));

}

// 绘制暂停

void RenderViewImpl::OnSetIsPaused(bool paused) {

  if (!webview())

    return;

  webview()->setIsPaused(paused);

}

// 文档解析暂停

void WebViewImpl::setIsPaused(bool paused) {

    Document* document = page()->mainFrame()->document();

    if (!document)

        return;

    if (paused)

        document->suspendScheduledTasks();

    else

        document->resumeScheduledTasks();

}

 

        问题二:测试中心页面不能收敛,此页面并没有动态元素,因此用上面的方法分析是不奏效的,需要找其他的方法。百度浏览内核是基于Blink 35版本再次开发的版本,因此想要验证下原生内核是否有问题,于是验证了Blink 3536都是不收敛,blink 37 版本可以收敛,因此可以断定是Blink 内核自己的bug,通过对绘制过程相关代码的查找,发现google 工程师对此问题的一个注释。

if (RenderView* view = renderView()) {

        ASSERT(!view->needsLayout());

        view->compositor()->updateCompositingLayers();

 

        // FIXME: we should not have any dirty bits left at this point. Unfortunately, this is not yet the case because

        // the code in updateCompositingLayers sometimes creates new dirty bits when updating direct compositing reasons.

        // See crbug.com/354100.

        view->compositor()->scheduleAnimationIfNeeded();

}

 

        通过上面的注释可以看出,在layer 有更新进行合成过程中,会引入脏数据导致layer 一直更新,并进行后面的数据处理和绘制,导致CPU 不收敛,通过增加打印Log 也可以证明,不收敛的页面确实在不断进行该函数的调用。

 

        通过对Blink 37 修复的patch 进行研究,内核对判断Layer 更新的状态进行了修改,调整了更新判断的结构,删除了scheduleAnimationIfNeeded 方法,通过其他状态判断是否需要更新layer,并进行数据的合成进行绘制,通过将37 patch 合入测试发现可以有效解决不收敛问题。

 

解决方案:

增加了CompositingReasonFinder::requiresCompositingForPosition 方法判断是否需要进行合成。

脏数据的问题还是没有根本解决,只是增加了判断,不会出现之前的循环处理。

void RenderLayerCompositor::assertNoUnresolvedDirtyBits()

{

    ASSERT(!compositingLayersNeedRebuild());

    ASSERT(!m_needsUpdateCompositingRequirementsState);

    ASSERT(m_pendingUpdateType == CompositingUpdateNone);

    ASSERT(!m_rootShouldAlwaysCompositeDirty);

ASSERT(!m_needsToRecomputeCompositingRequirements);

}

  

 五、总结

        CPU 收敛在CPU 性能测试中是一个非常重要的项目,对于用户体验的影响也是非常大。这个案例首先在CPU 收敛的测试方案上有很大的突破,通过较小的成本发现了不收敛的场景;其次,通过测试页面和弱网环境稳定复现bug,帮助RD定位问题,最后通过查阅blink 内核的官方文档、代码记录和bug 系统,最终找到了解决方案, 提升了整体产品的体验。


大咖招募
欢迎App研发/测试方面的大牛来投稿,开设专栏。我们提供丰厚的稿酬,预约个人专访,帮助建立个人技术品牌!
立即投稿

我要评论

字数不能超过140字,谢谢!
提交

评论({{allComments.length}})

{{comment.create_time.substr(0,16)}}

显示所有评论
复制成功!