Skip to content

性能分析工具实操

预计阅读 20 分钟
版本信息
最后更新:2026-07-09 · 适用基础库:≥ 3.9.0(最低兼容 ≥ 2.30.0) · 微信开发者工具:稳定版 1.06+

性能优化的前提是测量。本章手把手带你使用微信小游戏的主流性能分析工具,从开发者工具到真机测试全覆盖。


工具全景

工具适用阶段分析维度是否需要真机
微信开发者工具 Performance 面板开发期FPS/CPU/内存/渲染不需要
微信开发者工具 Audits 面板提审前综合评分 + 优化建议不需要
Cocos Creator Profiler开发期Draw Call / 节点数 / 纹理内存不需要
PerfDog测试期FPS/CPU/GPU/内存/网络/功耗必须真机
WeTest 云测测试期多机型兼容性 + 性能云真机

微信开发者工具 Performance 面板

录制步骤

  1. 打开微信开发者工具 → 选择小游戏项目。
  2. 点击「调试器」→「Performance」标签。
  3. 点击 ● 录制按钮 → 在模拟器中操作游戏 → 点击 ■ 停止。
  4. 分析火焰图(Flame Chart)中的函数调用耗时。

重点关注的指标

┌────────────────────────────────────────────┐
│ FPS (绿色条)     │ 目标: ≥ 55             │
│ CPU (蓝色区域)   │ 关注长任务 (Long Task)  │
│ Memory (紫色线)  │ 关注持续上涨趋势        │
│ Network          │ 关注大文件/并发请求     │
└────────────────────────────────────────────┘

常见性能瓶颈定位

typescript
// 使用 Performance API 在代码中手动埋点
class GamePerformance {
  private marks: Map<string, number> = new Map()

  /** 标记开始 */
  start(label: string): void {
    this.marks.set(label, Date.now())
  }

  /** 标记结束并输出耗时 */
  end(label: string): number {
    const start = this.marks.get(label)
    if (!start) return 0
    const elapsed = Date.now() - start
    console.log(`[Perf] ${label}: ${elapsed}ms`)
    this.marks.delete(label)
    return elapsed
  }

  /** 获取当前 FPS(基于 requestAnimationFrame 的滑动窗口估算) */
  private static frameTimestamps: number[] = []

  static tickFPS(): number {
    const now = Date.now()
    this.frameTimestamps.push(now)
    // 只保留最近 1 秒的帧记录
    while (this.frameTimestamps.length > 0 && this.frameTimestamps[0] < now - 1000) {
      this.frameTimestamps.shift()
    }
    return this.frameTimestamps.length
  }

  /** 获取当前内存占用 (MB),真机数据以 PerfDog 为准 */
  static getMemoryUsage(): number {
    const perf = wx.getPerformance?.()
    if (perf?.memory) {
      return perf.memory.usedJSHeapSize / 1024 / 1024
    }
    // 微信小游戏环境通常不支持 performance.memory,返回 -1 表示不可用
    return -1
  }
}

Cocos Creator Profiler

开启调试面板

typescript
// 任意脚本中
import { profiler } from 'cc'

// 显示调试信息:Draw Call / 顶点数 / 三角面 / 纹理内存
profiler.showStats()

// 或在编辑器中:预览窗底部工具栏 → 点击「调试信息」切换显示

关键指标

指标健康值说明
Draw Call≤ 100超过 100 会影响帧率
Tris (三角面)≤ 50K2D 游戏通常远低于此值
Texture Memory≤ 50MB纹理总占用量
Node Count≤ 500活跃节点数
Frame Time≤ 16.6ms单帧处理时间(60fps 下)

PerfDog 真机性能测试

PerfDog 是微信官方推荐的移动端性能测试工具,免费使用。

测试流程

  1. 下载并安装 PerfDog(Windows/macOS)。
  2. 手机 USB 连接电脑 → 开启 USB 调试模式。
  3. PerfDog 中选择微信 → 小游戏进程。
  4. 启动游戏 → 录制性能数据 5-10 分钟。
  5. 导出报告 → 分析各指标趋势。

关键指标解读

FPS           ████████████████████░░  均值 58 / 最大值 60 / 低帧率占比 2%
CPU Usage     ██████████░░░░░░░░░░░░  均值 15% / 峰值 35%
Memory (PSS)  ██████████░░░░░░░░░░░░  均值 180MB / 峰值 220MB
GPU Usage     ██████░░░░░░░░░░░░░░░░  均值 8% / 峰值 20%
Temperature   ████░░░░░░░░░░░░░░░░░░  均值 35°C / 峰值 38°C
PerfDog 测试建议
  1. 覆盖场景:至少测试「首页 → 核心玩法 → 结算 → 返回」一个完整循环。
  2. 长时间挂机:测试 30 分钟,观察内存是否持续上涨(内存泄漏)。
  3. 多机型覆盖:至少 1 台千元安卓(如 Redmi)+ 1 台 iPhone(如 iPhone 8)。
  4. 网络模拟:在弱网(3G)下测试 CDN 资源加载速度。

性能分析检查清单

在提审或正式发布前,逐项检查:

检查项方法通过标准
帧率PerfDog / DevTools FPS核心玩法 ≥ 55fps
内存PerfDog Memory峰值 ≤ 300MB, 无持续上涨
启动时间wx.reportPerformance / 手动计时首帧 ≤ 3s
包体构建产物大小主包 ≤ 4MB
Draw CallCocos Profiler / GPU 抓帧≤ 100
音频延迟手动测试点击到音效响起 ≤ 100ms
弱网表现Charles 限速 / 开发者工具3G 下 60s 内可进入游戏

性能调试的常见误区


📚 相关阅读


📝 课后练习

练习 1:使用 PerfDog 录制并分析游戏性能数据

题目: 按照以下步骤使用 PerfDog 完成一次完整的性能测试,并撰写分析报告:

  1. 连接一台安卓真机(建议千元机,如 Redmi 系列),安装 PerfDog
  2. 选择「微信 → 你的小游戏进程」开始录制
  3. 按以下流程操作游戏并记录:
    • 启动 → 等待首页渲染完成(记录首帧时间)
    • 进入核心玩法 → 连续游玩 5 分钟(关注 FPS 稳定性)
    • 故意触发高负载场景(如大量粒子特效、频繁碰撞检测)
    • 切后台 30 秒 → 再切回前台(观察内存恢复情况)
    • 正常结束录制
  4. 导出 PerfDog 报告,关键分析:
    • FPS 均值 / P95 / 低帧率占比
    • 内存峰值 / 是否有持续上涨(判断内存泄漏)
    • CPU 占用峰值场景
    • 切后台前后内存差异

参考答案:

分析报告应包含以下结构:

markdown
## 性能测试报告 — [游戏名] v[版本号]

### 测试环境

- 设备:Redmi Note 12 (8GB RAM, Android 14)
- 微信版本:8.0.x
- 录制时长:8 分钟

### 测试结果

| 指标      | 均值  | 峰值  | P95 | 评估                     |
| --------- | ----- | ----- | --- | ------------------------ |
| FPS       | 58.2  | 60    | 55  | ✅ 达标                  |
| 内存(PSS) | 180MB | 245MB | —   | ⚠️ 峰值接近 300MB 警戒线 |
| CPU       | 12%   | 35%   | 25% | ✅ 正常                  |
| 温度      | 36°C  | 39°C  | —   | ✅ 正常                  |

### 问题发现

1. 内存从 165MB 持续上涨至 245MB,可能存在纹理/对象未释放
2. 切后台后内存仅回收 10MB,期望回收更多

### 优化建议

1. 检查粒子特效的生命周期管理
2. 场景切换时强制调用 `wx.triggerGC()`
练习 2:手动埋点统计帧时间与 P99 计算

题目: 在游戏代码中实现一个帧时间统计器,完成以下功能:

  1. 在每帧开始和结束时记录时间戳
  2. 维护最近 300 帧(约 5 秒)的帧时间滑动窗口
  3. 每 60 帧输出一次统计报告(均值、P95、P99、最大值)
  4. 当 P99 帧时间超过 20ms 时输出警告
  5. 考虑使用循环缓冲区优化内存

参考答案:

typescript
// FrameTimeMonitor.ts
class FrameTimeMonitor {
  private readonly BUFFER_SIZE = 300
  private frameTimes: Float64Array // 使用 TypedArray 减少内存开销
  private writeIndex = 0
  private frameCount = 0
  private frameStart = 0

  constructor() {
    this.frameTimes = new Float64Array(this.BUFFER_SIZE)
  }

  /** 在每帧开始时调用 */
  beginFrame(): void {
    this.frameStart = performance.now()
  }

  /** 在每帧结束时调用 */
  endFrame(): void {
    const elapsed = performance.now() - this.frameStart
    this.frameTimes[this.writeIndex] = elapsed
    this.writeIndex = (this.writeIndex + 1) % this.BUFFER_SIZE
    this.frameCount++

    // 每 60 帧输出一次统计
    if (this.frameCount % 60 === 0) {
      this.report()
    }
  }

  private report(): void {
    const samples = this.getRecentSamples()
    samples.sort((a, b) => a - b)

    const avg = samples.reduce((s, v) => s + v, 0) / samples.length
    const p95Idx = Math.floor(samples.length * 0.95)
    const p99Idx = Math.floor(samples.length * 0.99)
    const p95 = samples[p95Idx]
    const p99 = samples[p99Idx]
    const max = samples[samples.length - 1]

    console.log(
      `[FrameTime] avg: ${avg.toFixed(1)}ms | ` +
        `P95: ${p95.toFixed(1)}ms | P99: ${p99.toFixed(1)}ms | ` +
        `max: ${max.toFixed(1)}ms | samples: ${samples.length}`
    )

    // P99 超过 20ms 警告(60fps 下正常为 ~16.67ms)
    if (p99 > 20) {
      console.warn(`⚠️ P99 帧时间 ${p99.toFixed(1)}ms 超过 20ms 警戒线,` + `可能存在性能瓶颈`)
    }
  }

  /** 获取最近的采样数据(排除未填充的缓存区位置) */
  private getRecentSamples(): number[] {
    const count = Math.min(this.frameCount, this.BUFFER_SIZE)
    const samples: number[] = []
    for (let i = 0; i < count; i++) {
      const idx = (this.writeIndex - 1 - i + this.BUFFER_SIZE) % this.BUFFER_SIZE
      samples.push(this.frameTimes[idx])
    }
    return samples
  }
}

// 在游戏循环中使用
const monitor = new FrameTimeMonitor()

function gameLoop() {
  monitor.beginFrame()

  // ... 游戏逻辑更新 ...

  monitor.endFrame()
  requestAnimationFrame(gameLoop)
}

关键设计点:

  • 使用 Float64Array 替代普通数组,避免 GC 压力
  • 循环缓冲区避免数组扩容开销
  • performance.now() 精度达到微秒级,适合帧级别测量

用心学习,持续实践