如何使用DevEco分析app的性能
性能优化、启动优化、内存优化、FPS监测、性能分析🧐
在鸿蒙OpenHarmony开发过程中,开发者开发的代码(Stage 模型)通常以调用 ArkUI 框架的代码为主,主要优化的代码部分也在其中,那么如何验证自己的代码是否起到了优化效果,就需要工具对比分析优化前和优化后的效果。
我们以一次测试过程为例,介绍如何分析性能。
构造场景,验证 RelativeContainer() 提升性能的能力
测试方法:我们使用对比测试的方法,比较使用 RelativeContainer() 前后可以对比的性能指标,来证明合理使用布局是对性能提升有帮助的。
测试工具:DevEco 中的 Profiler 工具。
图标以此对应可以监测的场景有:启动、帧率、耗时、内存。
测试的关键点如下:
- 将 Text 作为最小单元,Row 作为模拟嵌套层级使用的容器,RelativeContainer() 作为相对布局使用的容器。
- 将定义一组(10)颜色循环使用,这个颜色用做 Text 的背景色,用于直观地区分每个 Text 组件。(定义10个是为了代码更易读,也方便以10为单位扩展,生成20、30、40等数据📊)
- 使用 50 个 Text 组件连续嵌套,测试嵌套层级为 50 级的情况。Row() 是我们用于嵌套的容器。代码放在 Stack50View.ets
- 将 50 个 Text 组件平铺成 1 层,测试嵌套层级为 2 级( 50个Text层 + RelativeContainer()层)的情况。 RelativeContainer() 是我们用于减少布局嵌套层数的容器。代码放在 Relative50View.ets
- 对比第3点和第4点的渲染时间和内存情况,试图以此来证明使用 RelativeContainer() 布局可以优化性能。
定义的颜色如下:
const COLORS = [Color.Blue, Color.Green, Color.Pink, Color.Brown, Color.Grey,
Color.White, Color.Black, Color.Orange, Color.Yellow, Color.Red]
通过编码使得 Relative50View.ets 和 Stack50View.ets 能产生一样的视觉效果,在每个 Text 上编号以区分,效果如下:
然后,把他们放在一个list里,一共10行,效果如下:
测试结果:
启动分析,启动后6s内的性能分析
不使用 RelativeContainer() 的情况:
@Entry
@Component
struct Index {@State message: string = 'Hello World'build() {Column() {Stack50View()Stack50View()Stack50View()Stack50View()Stack50View()Stack50View()Stack50View()Stack50View()Stack50View()Stack50View()}}
}
使用 RelativeContainer() 的情况:
@Entry
@Component
struct Index {@State message: string = 'Hello World'build() {Column() {Relative50View()Relative50View()Relative50View()Relative50View()Relative50View()Relative50View()Relative50View()Relative50View()Relative50View()Relative50View()}}
}
内存分析,启动后6s内的内存分析
不使用 RelativeContainer() 的情况:
使用 RelativeContainer() 的情况:
帧率分析,上下滑动 list 时候的帧率变化
定义100个item放到list中
不使用 RelativeContainer() 的情况:
@Entry
@Component
struct Index {@State arr: number[] = Array.from(Array(100).keys()); build() {Column() {List() {ForEach(this.arr,(item) => {ListItem() {Stack50View()}},(item) => item.toString())}}}
}
使用 RelativeContainer() 的情况:
@Entry
@Component
struct Index {@State arr: number[] = Array.from(Array(100).keys());build() {Column() {List() {ForEach(this.arr,(item) => {ListItem() {// Stack50View()Relative50View()}},(item) => item.toString())}}}
}
结论:
这么看对比效果不佳。
对比项 | 启动分析(onCreate) | 内存分析 | 滑动时卡顿率(jank rate) |
---|---|---|---|
优化前 | 1.374ms | - | 8.2% |
优化后 | 1.697ms | - | 13.4% |
进一步测试
验证嵌套层级深度对性能有影响。
对比如下数据:
- 构建 50 层嵌套,只在第50层加一个text
- 构建 1 层嵌套,加一个text
对比项 | 启动分析(onCreate) | 内存分析 | 滑动时卡顿率(jank rate) |
---|---|---|---|
优化前 | 0.821ms | - | 0.5% |
优化后 | 0.027ms | - | 0.0% |
通过多组数据可以画出折线图:
结论:组件嵌套会增加渲染时间和内存。
对比测试
通过 Frame 监测列表下 render_service(1132) 中的 VSync-app 1374 中框选时间片段得到:
对比项 | 1层嵌套 | 10层嵌套 | 20层嵌套 | 30层嵌套 | 40层嵌套 | 50层嵌套 |
---|---|---|---|---|---|---|
time | - | - | - | 757.1ms | 512.4ms | 757.1ms |
occurrences | - | - | - | 68 | 47 | 68 |
FPS | - | - | - | 90 | 90 | 89 |
结论:从嵌套50层仍有89帧来看,说明嵌套对帧率变化影响不明显。
其他代码如下:
Relative50View.ets
const COLORS = [Color.Blue, Color.Green, Color.Pink, Color.Brown, Color.Grey,
Color.White, Color.Black, Color.Orange, Color.Yellow, Color.Red]@Builder function TextBuilder(index: number) {Text(`${index%10}`).fontColor(Color.White).fontSize(9).alignRules({top: { anchor: '__container__', align: VerticalAlign.Top },left: { anchor: '__container__', align: HorizontalAlign.Start }}).id(`figure${index}`).backgroundColor(COLORS[index%10]).align(Alignment.TopStart).height(70).margin({ top: 0, left: 5 * index })
}@Preview
@Component
export struct Relative50View {build() {Row() {Row() {RelativeContainerBuilder1()}.width('100%')}.height(100)}
}@Builder function RelativeContainerBuilder1() {RelativeContainer() {TextBuilder(0)TextBuilder(1)TextBuilder(2)TextBuilder(3)TextBuilder(4)TextBuilder(5)TextBuilder(6)TextBuilder(7)TextBuilder(8)TextBuilder(9)// 10TextBuilder(10)TextBuilder(11)TextBuilder(12)TextBuilder(13)TextBuilder(14)TextBuilder(15)TextBuilder(16)TextBuilder(17)TextBuilder(18)TextBuilder(19)// 20TextBuilder(20)TextBuilder(21)TextBuilder(22)TextBuilder(23)TextBuilder(24)TextBuilder(25)TextBuilder(26)TextBuilder(27)TextBuilder(28)TextBuilder(29)// 30TextBuilder(30)TextBuilder(31)TextBuilder(32)TextBuilder(33)TextBuilder(34)TextBuilder(35)TextBuilder(36)TextBuilder(37)TextBuilder(38)TextBuilder(39)// 40TextBuilder(40)TextBuilder(41)TextBuilder(42)TextBuilder(43)TextBuilder(44)TextBuilder(45)TextBuilder(46)TextBuilder(47)TextBuilder(48)TextBuilder(49)}
}
Stack50View.ets
const COLORS = [Color.Blue, Color.Green, Color.Pink, Color.Brown, Color.Grey,
Color.White, Color.Black, Color.Orange, Color.Yellow, Color.Red]@Preview
@Component
export struct Stack50View {build() {Row() {Row() {StackBuilder1()}.width('100%')}.height(100)}
}@Builder function TextBuilder(index: number, group: number) {Text(`${index%10}`).fontColor(Color.White).fontSize(9).backgroundColor(COLORS[index%10]).align(Alignment.TopStart).height(70)
}// 构建 1 层嵌套,加一个text
@Builder function StackBuilder3() {Row() {Text(`${50}`).fontColor(Color.White).fontSize(9).backgroundColor(COLORS[0]).align(Alignment.TopStart).height(50)}
}// 构建 50 层嵌套,只在第50层加一个text
@Builder function StackBuilder2(index: number) {Row() {if (index > 0) {StackBuilder2(index-1)} else {Text(`${50}`).fontColor(Color.White).fontSize(9).backgroundColor(COLORS[index%10]).align(Alignment.TopStart).height(50)}}
}// 构建 50 层嵌套,每一层增加一个text
@Builder function StackBuilder1() {Row() {TextBuilder(0, 0)Row() {TextBuilder(1, 0)Row() {TextBuilder(2, 0)Row() {TextBuilder(3, 0)Row() {TextBuilder(4, 0)Row() {TextBuilder(5, 0)Row() {TextBuilder(6, 0)Row() {TextBuilder(7, 0)Row() {TextBuilder(8, 0)Row() {TextBuilder(9, 0)// 10Row() {TextBuilder(10, 1)Row() {TextBuilder(11, 1)Row() {TextBuilder(12, 1)Row() {TextBuilder(13, 1)Row() {TextBuilder(14, 1)Row() {TextBuilder(15, 1)Row() {TextBuilder(16, 1)Row() {TextBuilder(17, 1)Row() {TextBuilder(18, 1)Row() {TextBuilder(19, 1)//20Row() {TextBuilder(20, 2)Row() {TextBuilder(21, 2)Row() {TextBuilder(22, 2)Row() {TextBuilder(23, 2)Row() {TextBuilder(24, 2)Row() {TextBuilder(25, 2)Row() {TextBuilder(26, 2)Row() {TextBuilder(27, 2)Row() {TextBuilder(28, 2)Row() {TextBuilder(29, 2)// 30Row() {TextBuilder(30, 3)Row() {TextBuilder(31, 3)Row() {TextBuilder(32, 3)Row() {TextBuilder(33, 3)Row() {TextBuilder(34, 3)Row() {TextBuilder(35, 3)Row() {TextBuilder(36, 3)Row() {TextBuilder(37, 3)Row() {TextBuilder(38, 3)Row() {TextBuilder(39, 3)// 40Row() {TextBuilder(40, 4)Row() {TextBuilder(41, 4)Row() {TextBuilder(42, 4)Row() {TextBuilder(43, 4)Row() {TextBuilder(44, 4)Row() {TextBuilder(45, 4)Row() {TextBuilder(46, 4)Row() {TextBuilder(47, 4)Row() {TextBuilder(48, 4)Row() {TextBuilder(49, 4)}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
}