智能客服
你问我答,随时在线为你解决问题
性能优化是指改进应用程序运行速度、资源利用效率和响应时间的系统性工作。通过优化可提升应用性能和稳定性。在数字化时代,随着应用程序的复杂性和规模不断增加,调优变得尤为重要。有效调优不仅能使应用程序更高效地运行,还能提高稳定性,提升效率,减少资源浪费,从而改善用户体验。因此,开发人员了解调优方法和常用工具至关重要。
调优过程包括现场复现、问题分析、确定解决方案和性能测试这几个关键步骤。现场复现是在具体环境中重现问题,以便更好地分析和解决。问题分析阶段深入分析应用程序的性能瓶颈和问题根源,为后续优化提供指导。确定解决方案是根据问题分析结果,制定具体的优化方案和措施。性能测试是验证调优效果的关键步骤,通过测试优化后的应用程序,评估改进效果。
为了有效进行调优工作,需要借助常用的工具。例如,性能分析工具DevEco Profiler可以监测应用的性能指标、录制Trace记录。开发者可以通过分析Trace数据,发现代码中的性能瓶颈,进而优化性能。
本文将介绍调优方法和常用工具,帮助开发者分析和解决应用程序性能问题,提升用户体验,确保应用程序高效稳定运行。
在日常开发中,需要关注的指标有完成时延、点击响应时延、滑动响应时延等,具体需要关注的指标可以参考《性能体验设计》。
调优分析方法在应用程序优化中起着关键作用,帮助开发人员识别问题、定位瓶颈并改进系统性能。具体调优分析方法如下:
调优分析是应用程序优化的关键步骤。通过现场复现、问题分析、确定解决方案和性能测试,开发人员可以识别并解决应用性能问题,提升应用程序的效率和稳定性。
性能调优工具能帮助开发者识别应用中的性能问题。DevEco Studio提供了多种场景化调优工具DevEco Profiler,具体如下:
其他常见的性能调优工具,包括HiDumper、SmartPerf。
DevEco Profiler工具不支持模拟器进行调优。
介绍
开发者可以使用Inspector双向预览,在DevEco Studio上查看应用在真机上的组件布局,并通过查看多次操作后的界面状态,快速分析定位状态变量、组件嵌套层次、UI界面布局存在的问题等。
使用方法
详细使用指导见:
HarmonyOS的DFX子系统提供了为应用框架以及系统底座核心模块的性能打点能力,每一处打点即是一个Trace,其上附带了记录执行时间、运行时格式化数据、进程或线程信息等。开发者可以使用DevEco Studio的Frame对Trace进行解析,并在其绘制的泳道图中识别关键渲染流程。
在HarmonyOS中,Trace记录的线程状态主要分为运行中(Running)、可运行(Runnable)、休眠中(Sleep)、IO阻塞下不可中断的睡眠态(Uninterruptible Sleep - IO)、不可中断的睡眠态(Uninterruptible Sleep - non IO)。其状态转化图如下:

Trace 会用不同的颜色来标识不同的线程状态,在每个方法上面都会有对应的线程状态来标识目前线程所处的状态,通过查看线程状态可以分析出当前的性能瓶颈。
(1)运行中(Running)

运行中(Running)表示处于该状态的线程才可能在CPU上运行。同一时刻可能有多个线程处于可执行状态,这些线程的task_struct结构被放入对应CPU的可执行队列中,每个线程最多出现在一个CPU的可执行队列中。调度器从各个CPU的可执行队列中选择一个线程在该CPU上运行。
(2)可运行(Runnable)

可运行(Runnable)表示线程可以运行但当前未被调度,在等待CPU。Runnable状态持续时间越长,说明CPU调度越忙,未能及时处理该任务。
(3)休眠中(Sleep)

休眠中(Sleep)表示线程没有工作,可能是因为在互斥锁上被阻塞,或在等待某些操作返回,通常是在等待事件驱动。
(4)IO阻塞下不可中断的睡眠态(Uninterruptible Sleep - IO)

IO阻塞下不可中断的睡眠态(Uninterruptible Sleep - IO)表示线程在I/O上被阻塞或等待磁盘操作完成。当系统处于低内存状态时,申请内存的时候可能会触发page fault,从而导致有大量的不可中断的睡眠态出现。在Linux系统的page cache链表中,有时会出现一些还没准备好的page(即还没把磁盘中的内容完全地读出来) ,而正好此时用户在访问这个page时就会出现page fault。
(5)不可中断的睡眠态(Uninterruptible Sleep - non IO)

不可中断的睡眠态(Uninterruptible Sleep - non IO)表示线程在其他内核操作(如内存管理)上被阻塞。线程陷入内核态,有时是正常现象,有时则需要进一步分析。
在HarmonyOS中,图形系统采用统一渲染模式,遵循典型流水线模式。以60Hz刷新率为例,每个Vsync周期为16.7ms;90Hz时,每个Vsync周期为11.1ms;120Hz时,每个Vsync周期为8.3ms。

在整个渲染流程中,应用侧首先响应消费者的屏幕点击等输入事件,处理完成后提交给Render Service。Render Service协调GPU等资源处理,最终将图像送到屏幕上显示。
其中应用侧的渲染流程如下图所示。了解ArkUI的渲染流程有助于定位应用侧的卡顿问题出现在哪个环节。

在整个处理流程中,应用侧和Render Service侧都可能出现卡顿,导致最终用户观察到丢帧。这两种情况分别称为AppDeadlineMissed和RenderDeadlineMissed。AppDeadlineMissed通常是由于应用逻辑处理代码不够高效导致的,而RenderDeadlineMissed则可能是因为界面结构过于复杂或GPU负载过大等原因引起的。这两个故障模型通过Frame模板可以直观地查看。相应的故障模型如下图所示。


一帧的渲染流程中的UI后端引擎的常用Trace的含义如下图所示:

各部分介绍见下表:
序号 | Trace | 参数说明 | 描述 |
|---|---|---|---|
1 | OnVsyncEvent now:%" PRIu64 " | 当前时间戳--纳秒级 | 收到Vsync信号,渲染流程开始 |
2 | FlushVsync | 刷新视图同步事件,包括记录帧信息、刷新任务、绘制渲染上下文、处理用户输入 | |
3 | UITaskScheduler::FlushTask | 刷新UI界面,包括布局、渲染和动画等 | |
4 | FlushMessages | 发送消息通知图形侧进行渲染 | |
5 | FlushLayoutTask | 执行布局任务 | |
6 | FlushRenderTask %zu | 当前页面上的需要渲染的节点的数量 | 总渲染任务执行 |
7 | Layout | 节点布局 | |
8 | FrameNode::RenderTask | 单个渲染任务执行 | |
9 | ListLayoutAlgorithm::MeasureListItem:%d | 当前列表项索引 | 计算列表项的布局尺寸 |
图形图像子系统中的Render Service负责界面内容的绘制,处理各应用提交的统一渲染任务,将不同应用的图层合并并送显。每个Vsync周期,Render Service首先处理应用提交的指令,包括渲染树节点的新增、删除和修改,然后进行动画计算和遮挡计算,以更新统一渲染树。接下来,对渲染树执行绘制,预处理每个节点,计算绝对位置和脏区信息,针对脏区进行绘制。优先使用硬件合成器绘制,无法合成时交由GPU执行重绘,所有结果存入屏幕缓冲区,最后提交送显并展示。
Vsync信号刷新时的Trace泳道图如下所示。

各部分介绍如下表:
序号 | Trace | 描述 |
|---|---|---|
1 | RSMainThread::DoComposition | 合成渲染树上各节点图层 |
2 | RSMainThread::ProcessCommand | 处理应用侧指令 |
3 | Animate | 动画处理 |
4 | ProcessDisplayRenderNode[x] | 单个显示器画面的绘制流程 |
5 | Repaint | 硬件合成器绘制 |
6 | RenderFrame | GPU执行绘制 |
7 | SwapBuffers | 刷新屏幕缓冲区 |
8 | Commit | 绘制结果提交上屏 |
懒加载使用LazyForEach实现。LazyForEach 从提供的数据源中按需迭代数据,并在每次迭代过程中创建相应组件。当LazyForEach在滚动容器中使用时,框架会根据滚动容器的可视区域按需创建组件。当组件滑出可视区域时,框架会销毁组件以降低内存占用。
下图展示了懒加载过程中一帧的Trace泳道图。

序号 | Trace | 参数说明 | 描述 |
|---|---|---|---|
1 | OnIdle, targettime:%" PRId64 " | 时间戳,在这个时间之前完成该任务 | 在idle事件循环中,框架会检查是否有新的事件需要处理。如果有新的事件,任务调度器会被加入到UI线程中,并执行预测任务。 |
2 | Builder:BuildLazyItem [%d] | 需创建的项目索引 | 在需要时创建项,并将其缓存。 |
3 | CustomNode:BuildRecycle %s | JS视图名称 | 触发重用渲染 |
4 | ExecuteJS | 执行JS代码操作。 | |
5 | List predict | 添加预测布局任务操作。 | |
6 | Layout | 完成当前帧节点的布局。 |
开发者可以根据业务需求,使用HiTraceMeter进行自定义Trace打点跟踪,具体使用细节可参考《使用HiTraceMeter跟踪性能(ArkTS/JS)》和《使用HiTraceMeter跟踪性能(C/C++)》。
添加自定义Trace后,可在SmartPerf-Host调试工具上查看。自定义Trace将以独立泳道的形式呈现在对应进程下。下图展示了两条泳道,使用了startTrace和finishTrace方法,表示程序运行过程中指定标签从调用startTrace到调用finishTrace的耗时统计。图中记录了CUSTOM_TRACE_TAG_1和CUSTOM_TRACE_TAG_2两个标签的耗时统计。
自定义Trace示例:

自定义状态值示例:
