• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 使用SmartPerf-Host分析应用性能
2
3## 简介
4
5SmartPerf-Host是一款深入挖掘数据、细粒度展示数据的性能功耗调优工具,可采集CPU调度、频点、进程线程时间片、堆内存、帧率等数据,采集的数据通过泳道图清晰地呈现给开发者,同时通过GUI以可视化的方式进行分析。该工具当前为开发者提供了五个分析模板,分别是帧率分析、CPU/线程调度分析、应用启动分析、TaskPool分析、动效分析。关于工具使用的更多内容可查看[SmartPerf-Host调优工具使用指导](../../device-dev/device-test/smartperf-host.md/)。
6
7本文提供一些性能分析示例,介绍如何使用帧率分析和应用启动分析两个模板采集数据、分析数据,从而发现性能优化点。
8
9## 本地部署
10
11使用SmartPerf-Host进行性能分析前,需要先完成本地部署,本地部署的详细指导请参考[如何编译TraceStreamer](https://gitee.com/openharmony/developtools_smartperf_host/blob/master/trace_streamer/doc/compile_trace_streamer.md)和[SmartPerf-Host编译部署指导](https://gitee.com/openharmony/developtools_smartperf_host/blob/master/ide/README_zh.md)。在本地部署成功后,可通过https://[部署机器ip地址]:9000/application/访问,如下图。
12
13**图1** 本地部署访问页
14
15![](./figures/smartperf-host-using-1.png)
16
17## 性能分析示例
18
19### **FrameTimeline**帧率分析
20
21SmartPerf-Host提供FrameTimeline帧率分析功能,可以抓取记录每一帧的渲染数据,自动标识其中的卡顿帧,并提供同时段的系统Trace信息,帮助开发者高效分析卡顿位置和原因。
22
23#### 场景示例
24
25如下场景代码使用了Grid来实现了一个网格布局,在应用界面上下滑动时发现有卡顿掉帧现象。下文基于这个场景来介绍FrameTimeline帧率分析功能的使用方式。
26
27```
28@Entry
29@Component
30struct Index {
31  @State children: number[] = Array.from<undefined, number>(Array(2000).fill(undefined), (_v: undefined, k) => k);
32  build() {
33    Scroll() {
34      Grid() {
35       ForEach(this.children, (item: number) => {
36          GridItem() {
37            Stack() {
38              Stack() {
39                Stack() {
40                  Text(item.toString())
41                    .fontSize(32)
42                }
43              }
44            }
45          }
46        }, (item: number) => item.toString())
47      }
48      .columnsTemplate('1fr 1fr 1fr 1fr')
49      .columnsGap(0)
50      .rowsGap(0)
51      .size({ width: "100%", height: "100%" })
52    }
53  }
54}
55```
56
57#### 抓取数据
58
59下面介绍使用FrameTimeline帧率分析模板抓取数据的步骤:
60
611. 打开Record template -> Trace template -> FrameTimeline模板的配置开关。
62
63	**图2** FrameTimeline模板配置
64
65	![](./figures/smartperf-host-using-2.png)
66
672. 自定义配置抓取时间、抓取数据大小和结果文件名称。
68
69	**图3** 抓取配置项
70
71	![](./figures/smartperf-host-using-3.png)
72
733. 点击右上角Record开始抓取,同时在设备上复现应用掉帧或卡顿的操作过程,抓取完成后页面会自动加载trace数据。
74
75**说明:** 
76
77- 在数据抓取和分析的过程中,请不要主动退出应用或者设备,否则可能导致分析任务失败。
78
79- 点击Record时,网站上方出现please kill other hdc-server!的提醒,表示设备连接失败,说明设备的hdc连接端口被占用,需要在cmd命令行中执行hdc kill指令,然后再重新连接设备进行抓取。
80
81#### 分析数据
82
83完整的一个渲染流程,首先是App侧响应用户输入完成UI绘制,然后提交给Render Service,由Render Service协调GPU等资源完成渲染、合成和送显操作,在这个过程中App侧和Render Service侧都有可能出现卡顿最终导致丢帧现象。
84
85通过图4、图5、图6三组泳道数据,开发者们可以快速发现丢帧的位置,并完成初步的定界。
86
87**图4** UI + RenderService总耗时
88
89![](./figures/smartperf-host-using-4.png90
91
92**图5** UI耗时
93
94![](./figures/smartperf-host-using-5.png95
96
97**图6** RenderService耗时
98
99![](./figures/smartperf-host-using-6.png100
101- Expected Timeline是理想帧泳道图,Actual Timeline是真实帧泳道图。
102
103- 绿色帧为正常帧,橙色帧为卡顿帧,黄色帧表示RS进程与App进程起止异常。
104
105- UI耗时(图5)显示了应用侧每一帧的处理耗时,方块的长度即为具体的耗时,RenderService耗时(图6)同理。
106
107- App侧帧/RS侧帧卡顿的计算标准为帧的实际结束时间晚于帧的期望结束时间即为卡顿。
108
109- App侧有橙色出现,需要审视UI线程的处理逻辑是否过于复杂或低效,以及是否被其它任务抢占资源。
110
111- RS侧有橙色出现,需要审视界面布局是否过于复杂,可以使用布局检查器ArkUI Inspector工具和HiDumper命令行工具辅助分析定位,相关指导可以参考[使用HiDumper命令行工具优化性能](./performance-optimization-using-hidumper.md/)。
112从图5和图6结合来看可以确定场景示例明显属于App侧的帧卡顿。点击卡顿帧进行详细分析,相应的关联帧会通过线连起来,同时在Current Selection显示它的Details信息,如图7。
113
114**图7** App卡顿帧
115
116![](./figures/smartperf-host-using-7.png)
117
118- Duration表示帧的持续时间。
119
120- Jank Type表示卡顿类型。APP Deadline Missed表示应用侧卡顿。
121
122- FrameTimeLine flows Slice表示链接FrameTimeLine关联帧。
123
124- Preceding flows Slice表示链接RS关联帧。
125
126如下图,展开的应用泳道图中,存在两个名字和Pid一样的泳道,第一个为线程的使用情况,第二个为线程内的方法栈调用情况。结合卡顿帧对应时间段的Trace数据,可以定位到FlushLayoutTask耗时过长,它的作用是重新测量和布局所有的Item。其中Layout[Gird]耗时最久,因此卡顿原因可以确定为Gird布局处理逻辑过于复杂或低效。
127
128**图8** 应用布局绘制trace数据
129
130![](./figures/smartperf-host-using-8.png131
132定位到Grid布局代码段,经过分析,去除了冗余的3层stack容器,并将源数据提前处理为布局中需要的string类型,减少布局消耗。同时给Grid添加cachedCount参数结合LazyForEach进行预加载,cachedCount的值设定为一屏能够渲染的GridItem数量。优化后采用同样的方式抓取数据,得到的FrameTimeline泳道数据如图9,并且滑动过程中无卡顿丢帧现象。
133
134**图9** 优化后FrameTimeline泳道图
135
136![](./figures/smartperf-host-using-9.png137
138优化后的示例代码如下:
139
140```
141class MyDataSource implements IDataSource { // LazyForEach的数据源
142  private list: string[] = [];
143
144  constructor(list: string[]) {
145    this.list = list;
146  }
147
148  totalCount(): number {
149    return this.list.length;
150  }
151
152  getData(index: number): string {
153    return this.list[index];
154  }
155
156  registerDataChangeListener(_: DataChangeListener): void {
157  }
158
159  unregisterDataChangeListener(): void {
160  }
161}
162@Entry
163@Component
164struct Index {
165  @State children: string[] = Array.from<undefined, string>(Array(2000).fill(undefined), (_v: undefined, k) => k.toString());
166  @State data: MyDataSource = new MyDataSource(this.children)
167  build() {
168    Scroll() {
169      Grid() {
170        LazyForEach(this.data, (item: string) => {
171          GridItem() {
172            Text(item)
173              .fontSize(32)
174          }
175        }, (item: string) => item)
176      }
177      .cachedCount(80)
178      .columnsTemplate('1fr 1fr 1fr 1fr')
179      .columnsGap(0)
180      .rowsGap(0)
181      .size({ width: "100%", height: "100%" })
182    }
183  }
184}
185```
186
187### **AppStartup**应用启动分析
188
189SmartPerf-Host提供了AppStartup功能,以便于分析应用启动时各个阶段耗时情况。应用启动分析功能主要是提供应用启动分析模板,帮助系统调优人员做应用启动慢场景问题分析,快速查找系统侧启动慢阶段和耗时长调用栈信息。
190
191#### 场景示例
192
193以下示例代码展示AppStartup功能。
194
195```
196@Entry
197@Component
198struct Index {
199  @State private text: string = "hello world";
200  private count: number = 0;
201
202  aboutToAppear() {
203    this.computeTask();
204  }
205
206  build() {
207    Column({space: 10}) {
208      Text(this.text).fontSize(50)
209    }
210    .width('100%')
211    .height('100%')
212    .padding(10)
213  }
214
215  computeTask() {
216    this.count = 0;
217    while (this.count < 10000000) {
218      this.count++;
219    }
220  }
221}
222```
223
224#### 抓取数据
225
226使用如下步骤进行AppStartup数据的抓取:
227
2281. 切换到Flags页面,将AppStartup选项切换到Enabled,开启AppStartup模板。
229
230	**图10** AppStartup特性开关
231
232	![](./figures/smartperf-host-using-10.png233
2342. 切换到Record template页面,点击Trace template,开启AppStartup。
235
236	**图11** AppStartup模板配置
237
238	![](./figures/smartperf-host-using-11.png239
2403. Record setting内设置文件名、大小以及抓取时长。
241
242	**图12** 抓取配置项
243
244	![](./figures/smartperf-host-using-12.png245
2464. 点击右上角Record开始抓取,同时在设备上打开目标应用。可提前点击StopRecord完成抓取,或者等待时间自动完成抓取。抓取完成后会页面会自动加载trace数据。
247
248	**图13** 停止抓取选项
249
250	![](./figures/smartperf-host-using-13.png251
252#### 分析数据
253
254等待分析结果自动生成。点击右上角的筛选按钮,选中AppStartup,便于查看分析。
255
256**图14** 模板数据筛选
257
258![](./figures/smartperf-host-using-14.png259
260展开对应应用的泳道,找到应用启动时的时间段。选中AppStartup泳道全部阶段,可以在下方详情内看到具体阶段的耗时情况。
261
262**图15** AppStartup各阶段耗时情况——优化前
263
264![](./figures/smartperf-host-using-15.png265
266- ProcessTouchEvent:点击事件输入及处理
267
268- StartUIAbilityBySCB:处理创建进程信息&创建窗口
269
270- LoadAbility:拉起进程
271
272- Application Launching:加载应用
273
274- UI Ability Launching:加载UI Ability
275
276- UI Ability OnForeground:应用进入前台
277
278- First Frame - App Phase:首帧渲染提交-应用
279
280- First Frame - Render Phase:首帧渲染提交-Render Service
281
282上图展示结果显示,执行耗时最长的是UI Ability OnForeground阶段。目前耗时Duration为323ms。
283
284**图16** UI Ability OnForeground阶段耗时——优化前
285
286![](./figures/smartperf-host-using-16.png287
288在这个阶段里,通过阶段内下方泳道可以发现生命周期aboutToAppear耗时较长,点击该泳道内容可以看到具体耗时Duration,为268ms,占整个UI Ability OnForeground阶段的82%。
289
290**图17** aboutToAppear耗时——优化前
291
292![](./figures/smartperf-host-using-17.png293
294查看代码后发现,在aboutToAppear生命周期函数内执行了耗时的计算任务,导致应用冷启动耗时长。
295
296随后对aboutToAppear内容进行异步延迟处理。优化后代码如下:
297
298```
299@Entry
300@Component
301struct Index {
302  @State private text: string = "hello world";
303  private count: number = 0;
304
305  aboutToAppear() {
306    setTimeout(() => {
307      this.computeTask();
308    }, 0)
309  }
310
311  build() {
312    Column({space: 10}) {
313      Text(this.text).fontSize(10)
314    }
315    .width('100%')
316    .height('100%')
317    .padding(10)
318  }
319
320  computeTask() {
321    this.count = 0;
322    while (this.count < 10000000) {
323      this.count++;
324    }
325  }
326}
327```
328
329处理后用同样的方式获取一遍数据。
330
331**图18** AppStartup各阶段耗时情况——优化后
332
333![](./figures/smartperf-host-using-18.png334
335继续聚焦到aboutToAppear生命周期所在的UI Ability OnForeground阶段,目前耗时Duration为81ms。
336
337**图19** UI Ability OnForeground阶段耗时——优化后
338
339![](./figures/smartperf-host-using-19.png340
341在这个阶段里,通过阶段内下方泳道可以发现需要查看的生命周期aboutToAppear,点击该泳道内容可以看到具体耗时Duration,为2ms,目前只占整个UI Ability OnForeground阶段的2.5%。
342
343**图20** aboutToAppear耗时——优化后
344
345![](./figures/smartperf-host-using-20.png)
346
347<!--no_check-->