• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 应用性能问题分析指导
2
3<!--Kit: Common-->
4<!--Subsystem: Demo&Sample-->
5<!--Owner: @mgy917-->
6<!--Designer: @jiangwensai-->
7<!--Tester: @Lyuxin-->
8<!--Adviser: @huipeizi-->
9
10## 概述
11
12常见的应用性能问题,开发者可以从丢帧问题(如滑动列表时的丢帧、窗口动画不连贯)和响应速度问题(如应用启动白屏过长、滑动不跟手)两个角度去分析。在分析性能问题之前、验证性能优化方案之时,开发者会需要对应用的性能指标做检测。本文会先说明丢帧和响应速度问题相关的性能指标检测方法,然后再阐述两个角度下,略有不同的问题分析思路。
13
14本文主要是以Trace数据为切入点进行分析,相应的工具可以使用SmartPerf Host或DevEco Studio内置的Frame等。若开发者需要补充SmartPerf Host工具和Trace相关知识,可以分别参考《[性能优化工具SmartPerf-Host](performance-optimization-using-smartperf-host.md)》和《[常用trace使用指导](common-trace-using-instructions.md)》等应用开发文档。
15
16## 应用性能指标
17
18### 平均帧率
19开发者可以通过HiTrace命令工具抓取应用刷新时的Trace信息,并通过SmartPerf Host集成性能工具分析。例如,抓取某应用发生丢帧处的Trace信息如图1,则平均帧率计算过程如下:
20
21**图1 应用丢帧处的Trace数据**
22
23 ![alt text](figures/application-performance-guide-1.png)
24
25其中,帧率 = Occurrences / Selected range。
26
27| 计算帧率时,可参考的Trace标签	| 含义| 	帧数| 	平均帧率 |
28| --- | --- | --- |--- |
29| RSMainThread::DoComposition| 	合成渲染树上各节点图层| 	20| 	32.5 |
30| Repaint	| 硬件合成器合成绘制 | 	18 | 	29.27 |
31
32如果依据RSMainThread::DoComposition标签,从GPU合成图层的角度,去模拟最终呈现给用户的上屏帧数,平均帧率为32.5FPS。
33
34
35如果依据Repaint标签,从硬件合成器合成绘制的角度,去模拟最终呈现给用户的上屏帧数,平均帧率为29.27FPS。
36
37
38可见,不同trace标签的选取,对估算用户真实感知到的平均帧率,是略有不同的。
39
40在了解估算原理后,值得说明的是,SmartPerf Host针对部分标签,已经可以实现框选时间范围内平均帧率的自动计算。例如,框选的范围内存在RSMainThread::DoComposition泳道,平均帧率自动计算效果如图2所示,而其相应的平均帧率算法如下:
41
42帧数 = 框选时间范围内,完整的DoComposition个数。
43时间 = 框选时间范围内,从第一个完整的DoComposition开始到最后一个完整的DoComposition结束的时间。
44
45进而通过,平均帧率 = 帧数 / 时间,计算平均帧率。
46
47**图2 SmartPerf Host平均帧率自动计算**
48
49 ![alt text](figures/application-performance-guide-2.png)
50
51### 完成时延
52
53这里以点击应用tab,做页面跳转的场景为例,通过Trace分析其完成时延。
54在了解如何通过Trace分析应用的完成时延之前,开发者需要简单了解OpenHarmony中图形渲染的流程。在整个渲染流程中,首先由App侧响应用户的屏幕输入事件,由App侧处理完成后再提交给RS侧,由RS侧协调GPU等资源处理后,再将最终的图像送到屏幕上进行显示。图3即该过程中,几个常见的进程/标签名,及其在system侧、App侧、RS侧的时序分布。后文将结合Trace图示例,与标签含义说明,简要描述system侧、App侧、RS侧在图形渲染流程中的行为。
55
56**图3 页面跳转完成时延**
57
58 ![alt text](figures/application-performance-guide-3.png)
59
60在系统侧,如图4所示,用户点击屏幕后,CPU会收到硬件中断;aptouch进程会响应中断,判断哪些像素被点击、是否是手势;如果是手势,多模会收到手势信号,形成点击事件,执行应用注册的事件回调。
61
62**图4 system侧Trace**
63
64 ![alt text](figures/application-performance-guide-4.png)
65 | 系统侧进程标签 | 	含义 |
66 | --- | 	--- |
67 | irp	 | CPU收到硬件中断 |
68 | aptouch_daemon | 	响应中断,哪些像素被点,判断是不是手势 |
69 | mmi_service	 | 多模收到手势,会执行应用注册的回调 |
70
71在app侧,如图5所示,应用收到点击,运行onClick中的回调,UI后端引擎会在测量、布局做完后,将组件树信息序列化后传给RS侧。
72
73**图5 app侧Trace**
74
75 ![alt text](figures/application-performance-guide-5.png)
76 |APP侧trace标签示例 |	含义 |
77 | --- |	--- |
78 |DispatchTouchEvent |	应用收到事件,开始运行onClick的回调 |
79 |MarshRSTransactionData cmdCount:36 transactionFlag:[7291,51] |	App将组件树信息序列化后发给RS |
80
81图形图像子系统中的Render Service,是负责界面内容绘制的部件,如图6所示,它收到App侧序列化的组件树信息后,将其反序列化,更新统一渲染树后,翻译为GPU绘制指令,最后将GPU绘制好的图层,放到硬件合成器上做合成,层层堆叠,最终显示到屏幕上。
82
83**图6 RS侧Trace**
84
85 ![alt text](figures/application-performance-guide-6.png)
86
87RS侧trace标签示例	含义
88RSMainThread::ProcessCommandUni [7291,51]	反序列化组件树信息
89RenderFrame	将渲染树翻译为GPU绘制指令
90Repaint	硬件合成器合成绘制。
91
92App侧序列化与RS侧反序列化的Trace示例标签中都有 [7291,51],分别是线程号与帧编号标识。可以看到被处理的帧信息,是按编号在App侧与RS侧一一对应的。
93
94不同开发者分析问题的目的不同,所关注的问题层级和入口也会不同,因此对响应时间的起点与终点的界定也有可能不同。这里以系统行为的硬件中断为起点,RS侧硬件合成器合成绘制、提交上屏为终点,测量响应时间。
95
96**图7 测量完成时延**
97
98 ![alt text](figures/application-performance-guide-7.png)
99
100如图7所示,该点击事件完成时延约为138.4ms。
101
102## 应用性能分析
103
104### 丢帧问题分析思路
105
106这里以某应用滑动场景发生丢帧为例,简要说明应用侧问题分析思路。
107
108**信息准备**
109
110**确定问题现象**
111
112用户环境版本、数据量是怎样?用户做了什么操作?是否可以本地复现?复现概率如何?
113
114**抓取所需日志信息**
115
116HiTrace、HiPerf、cpuProfiler、常规log等各类可观测性数据。
117
118**问题分析**
119
120导致应用丢帧的原因非常多,可能是应用本身原因,可能是系统原因,也有可能是硬件层原因。不同卡顿原因在Trace中有不同表现,识别需要大量经验积累。
121
122分析过程,主要是结合App主进程和RenderService渲染进程Trace数据,先排查系统、硬件是否异常,再分析应用本身原因。
1231.	看线程状态和运行核,看是否被其他进程抢占资源,排除系统侧运行异常。
124
125    **看线程状态**
126
127    从图8可以看到,应用线程大部分时间处于Running状态,无特殊异常。
128
129    **图8 丢帧处应用主线程状态**
130
131    ![alt text](figures/application-performance-guide-8.png)
132
133    **看运行频率**
134
135    查看关键任务是否跑在了小核,以低频运行。从Thread Usage信息栏,如图9所示,可以看到丢帧处应用线程和前面正常帧类似,都主要运行在大核上。其运行频点,可以参考Freq Usage信息栏,如图10所示。
136
137    **图9 丢帧处应用主线程运行核**
138
139    ![alt text](figures/application-performance-guide-9.png)
140
141    **图10 Freq Usage频点信息**
142
143    ![alt text](figures/application-performance-guide-10.png)
144
145    出于兼顾高性能、低功耗的需求,多核工程机常采用异构架构设计,根据CPU频率,区分大中小核等。
146
1472.	找到 Trace中每一帧耗时的部分,大致定位是App侧问题还是RS侧问题,并结合Trace标签,初步定位原因。
148    从图11中橙色丢帧的位置,可以看到耗时主要在App侧,是BuildLazyItem方法耗时较长导致,可以大致推测,是列表懒加载时,Item里的自定义组件绘制时间较长导致的。
149
150    **图11 应用主线程与RenderService线程的TimeLine**
151
152     ![alt text](figures/application-performance-guide-11.png)
153
1543.	结合cpuProfiler查看ArkTS函数调用栈信息,或其他日志信息,排查应用代码。
155    例如,可以结合DevEco Studio中Frame工具,方便地跳回源码,定位具体是哪一个自定义组件绘制时间较长。
156
157    **图12 Frame工具抓取应用信息图**
158
159     ![alt text](figures/application-performance-guide-12.png)
160
161### 解决方案
162关于应用的流畅度优化,开发者可以尝试从如下几个方面入手:
163
164- 如果定位是App侧问题,需要进一步审视在UI线程中的处理逻辑,是否过于复杂或低效。
165- 如果定位是RS侧问题,需要进一步审视是否是界面布局过于复杂。
166
167最终,根据卡顿原因,结合业务场景和API找出适合解决方案,并用Trace等数据验证优化结果。
168
169### 响应速度问题分析思路
170
171这里以冷启动场景的响应速度为例,做具体分析思路的说明。
172
173**信息准备**
174
175**确定问题现象**
176
177用户环境版本、数据量是怎样?用户做了什么操作?体验到了什么现象?是否可以本地复现?
178
179**明确测试标准**
180
181为什么体验到这个现象会觉得性能有问题?衡量响应速度的起点和终点是哪里?
182
183**抓取所需日志信息**
184
185HiTrace、HiPerf、cpuProfiler、常规log等各类可观测性数据。
186
187**问题分析**
188
189分析应用冷启动场景,首先开发者需要简单了解OpenHarmony应用启动流程,如图13所示,大致分为五个阶段:
190**图13 OpenHarmony应用启动流程**
191
192 ![alt text](figures/application-performance-guide-13.png)
193
1941.	AbilityManageService请求AppSpawn创建应用进程。
1952.	AppManageService触发应用启动流程、应用进程加载应用包。
1963.	AppManageService触发Ability启动流程、应用进程加载Ability资源、根据应用生命周期定义,触发生命周期回调。
1974.	创建UI Ability持有的Windows对象。
1985.	绘制UI界面首帧。
199
200这五个阶段,对应Trace中的标签信息如下:
201
202| 生命周期	| trace标签|
203| ---	| ---|
204| 创建应用进程	| AppManageService::StartAbility()|
205| 启动应用	| OHOS::AppExecFwk::MainTread::HandleLaunchApplication()|
206| 启动UI Ability	| OHOS::AppExecFwk::MainTread::HandleLaunchAbility()|
207| 创建窗口	| AbilityMonitor::OnWindowStageCreate()|
208| 绘制应用UI界面首诊	| JsRuntime::RunScript() RSMainThread::SendCommands()|
209
210而实际分析时,开发者可以借助SmartPerf Host提供的AppStartup模板,将各启动阶段自动拆解出来,如图14所示。关于AppStartup模板详细使用方法,请参考《[性能优化工具SmartPerf-Host](performance-optimization-using-smartperf-host.md)》中相关章节。
211
212**图14 AppStartup泳道图展示**
213
214 ![alt text](figures/application-performance-guide-14.png)
215
216之后,开发者可以结合应用启动各阶段Trace信息,对比应用前一个版本或竞品表现,找出差异点,大致分析是哪阶段时间增加了。
217
218**分析系统耗时点**
219
220如果分析是系统的问题,则查看系统对应的部分,一般情况要优先查看系统是否异常,例如:
221- 查看关键任务是否跑在了小核(App线程处于 Running 状态,但是执行耗时变长)。
222- 查看频率是否没有跑满 (App线程处于 Running 状态,但是执行耗时变长)。
223- 是否是高负载场景:查看 CPU 使用率,是否已经被跑满(App线程有大段的 Runnable 状态)。
224- 是否是低内存场景:查看是否低内存 (App线程有大段的 Uninterruptible Sleep 状态)。
225
226**分析应用耗时点**
227
228如果分析是应用的问题,则结合上面耗时的点,查看应用对应的部分,例如:
229- 是否某一段方法自身执行耗时比较久(App线程处于 Running 状态)。
230- 是否在等待子线程或子进程返回数据(App线程处于 Sleep 状态)。
231
232**解决方案**
233
234一个性能问题点,在复杂应用中,通常会涉及多个问题。首先要把影响最大的因素找出来优化,影响比较小的因素可以先忽略。
235如果判断是系统的原因,首先看应用自身是否可以规避,否则转由系统来处理。
236如果判断是应用自身的原因,可以结合cpuProfiler查看函数调用信息,审查源码,摸索解决之道。
237应用的响应速度优化大多集中在:UI界面、视觉动效、指令逻辑等方面,开发者可以尝试从如下几个方面入手:
238
239- UI优化:从UI布局渲染角度,加快应用绘制性能,比如减少布局嵌套,减少元素渲染,缓存UI动效等。
240- 按需加载优化:根据界面展示或模块加载的需要,延迟加载相关内容,从而减少对首帧页面的性能消耗。
241- 并发优化:使用系统并发执行多个任务的能力,减少响应过程中任务执行的整体时间。
242- 代码逻辑优化:在相关生命周期中减少冗余、避免耗时,提升执行效率,包括善用数据结构、缓存、优化调整时序等。
243- 视觉感知优化:通过交互设计的优化,利用动效动画的形式,在视觉层面提升用户响应速度的感知。
244最终,根据延迟原因,结合业务场景和API,找出适合解决方案,并用Trace等数据验证优化结果。
245
246
247## 总结
248文章首先介绍了通过分析Trace的方式,检测应用问题所对应的基础性能指标:
249针对丢帧问题,展示了如何结合Render Service进程下相关线程的Trace标签,估算用户真实感知到的平均帧率。
250针对响应速度问题,展示了如何结合OpenHarmony图形渲染流程相关Trace标签,估算用户真实感知到的点击完成时延。
251文章还从丢帧问题和响应速度问题两个角度,对常见应用性能问题进行分析,总结两个角度共通步骤大致如下:
2521.	信息准备:确定问题现象、明确问题标准、抓取相关可观测性数据。
2532.	问题分析:参考相关可观测性数据,先分析排查硬件、系统原因,再分析诊断应用自身原因,大致定位问题。
2543.	解决方案:依据问题分析定位,回归代码本身,结合业务场景和API,找出适合解决方案,并使用相关可观测性数据验证优化方案。
255