实战技巧 DevEco Profiler 性能调优 Time 
背景 
DevEco Studio 开发工具中提供了 Profiler 面板,可以让我们在针对实际开发应用过程中碰到的一些性能相关的问题提供解决方案。如响应速度慢、动画卡顿、内存泄漏、发热、耗电快等等场景。其中 Profiler 提供了实时监控、深度录制等监控过程的功能。从分析的角度入手,主要有以下几个纬度进行分析:
| 场景 | 面板 | 
|---|---|
| 基础耗时分析 | Time | 
| 基础内存分析 | Allocation | 
| 内存泄露分析 | Snapshot | 
| CPU活动分析 | CPU | 
| 冷启动分析 | Launch | 
| 并行并发分析 | Concurrency | 
| 加载丢帧分析 | ArkWeb | 
| 网络诊断 | Network | 

不过需要注意的是Profiler只能配合真机来使用,模拟器暂时不支持。
Time 
Time面板常见的需求是对应用中的函数耗时进行分析。函数作为应用开发基本的基石,一般常见的耗时工作都是放在函数中进行的。
Time可以对同步函数和异步函数进行分析。还提供了很方便的一键跳转到源码的功能,给开发者调试代码提供了很高效率的保证。
调试素材 
为了方便调试,我们提供了以下素材。

import { promptAction } from '@kit.ArkUI'
@Entry
@Component
struct Index {
  fn1() {
    this.fn2()
  }
  fn2() {
    this.fn3()
  }
  fn3() {
    this.fn4()
  }
  fn4() {
    promptAction.showToast({ message: `快速任务结束` })
  }
  build() {
    Column() {
      Button("快速任务")
        .onClick(() => {
          this.fn1()
        })
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}设置函数名称 
在模块的build-profile.json5中添加以下配置,否则采集到的函数可能都是匿名函数
entry/build-profile.json5 中 设置 strip 为false
{
  "apiType": "stageMode",
  "buildOption": {
    "nativeLib": {
      "debugSymbol": {
        "strip": false
      }
    }
  },选择应用和进程 
点击菜单中的Profiler,然后选择对应的设备和应用,进程会自动选中的。

然后选中Time菜单

开始跟踪,定位耗时任务 
- 一切准备就绪后,点击创建 Session 
- 然后点击 按钮开始录制  
- 在录制过程中,开始使用你的应用来重现问题。操作完毕后,记得点击结束。完成这次流程的录制。  
Time面板介绍 
在点击结束录制后,便能看到这个画面

ArkTS Callstack 
方舟运行时函数调用泳道,基于时间轴展示CPU使用率和虚拟机的执行状态,以及当前调用栈名称和调用类型。由于隐私安全政策,已上架应用市场的应用不支持录制此泳道。
调用栈分类从语言层面分为ArkTS、NAPI以及Native,从归属层面分为开发者代码以及系统代码。从这两个方面可以将调用栈类型归类如下:
- ArkTS:程序正在执行ArkTS代码; 
- NAPI:程序正在运行的NAPI代码; 
- Native:程序正在执行的Native代码; - 其中每一个类型的亮色和灰色分别代表开发者和系统的代码。 

- ArkTs Callstack 包含有开发者自己写的代码。点击它,会在下方显示详情面板 
- Weight表示函数的总耗时,Self表示函数自身的耗时。如  
- Heaviest Stack 表示是Details区域选择节点所处的耗时最长的完整调用栈 
- 绿色部分表示开发者自己编写的代码,可以看见右侧还有对应的 fn1、fn2、fn3 等等。通过这个函数调用栈便可获知哪个函数比较行耗时了 
- 此时,如果想要快速定位到源码,双击函数即可  
User Trace 
Time面板一般直接用来分析同步代码,如果想要分析和定位异步代码,建议搭配 hiTraceMeter接口配套User Trace使用
hiTraceMeter接口
| 接口名 | 描述 | 
|---|---|
| hiTraceMeter.startTrace(name: string, taskId: number) | 异步时间片跟踪接口,标记一个预跟踪耗时任务的开始。taskId是trace中用来表示关联的ID,如果有多个name相同的任务并行执行,则每次调用startTrace的taskId不同;如果具有相同name的任务是串行执行的,则taskId可以相同。 | 
| hiTraceMeter.finishTrace(name: string, taskId: number) | 异步时间片跟踪接口,name和taskId必须与流程开始的hiTraceMeter.startTrace对应参数值保持一致。 | 
调试素材 
import { hiTraceMeter } from '@kit.PerformanceAnalysisKit'
@Entry
@Component
struct Index {
  sleep(n: number) {
    const promise = new Promise<void>(resolve => {
      setTimeout(() => {
        resolve()
      }, n * 1000)
    })
    return promise
  }
  async asyncCb1() {
    hiTraceMeter.startTrace("异步长时任务", 1)
    await this.sleep(1)
    hiTraceMeter.finishTrace("异步长时任务", 1)
    await this.asyncCb2()
  }
  async asyncCb2() {
    hiTraceMeter.startTrace("异步长时任务", 2)
    await this.sleep(2)
    hiTraceMeter.finishTrace("异步长时任务", 2)
    await this.asyncCb3()
  }
  async asyncCb3() {
    hiTraceMeter.startTrace("异步长时任务", 3)
    await this.sleep(3)
    hiTraceMeter.finishTrace("异步长时任务", 3)
  }
  build() {
    Column() {
      Button("异步长时任务")
        .onClick(async () => {
          await this.asyncCb1()
          AlertDialog.show({ message: JSON.stringify('结束', null, 2) })
        })
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}
分析 

- 展开 User Trace
- 可以看见 User Trace泳道上存在粉色的3个异步任务
- 点击以上随便一个,可以在detail中看见这个函数的具体耗时
总结 
开发应用或服务过程中,如果遇到卡顿、加载耗时等性能问题,开发者通常会关注相关函数执行的耗时情况。DevEco Profiler提供的Time场景分析任务,可在应用/服务运行时,展示热点区域内基于CPU和进程耗时分析的调用栈情况,并提供跳转至相关代码的能力,使开发者更便捷地进行代码优化。