Skip to content

Stage05|Performance ​

Lesson057|Creator Profiler 应该怎么看? ​

Tags: #Creator2.x #Stage05 #Profiler #MeasurementDifficulty: ⭐⭐⭐⭐☆


真实问题:截图里 FPS 是 42,你能得出什么结论 ​

几乎什么都不能。缺少:

  • 哪台设备;
  • 哪个构建;
  • 正在执行什么操作;
  • 是否冷启动;
  • 采样多长;
  • CPU 还是 GPU;
  • 是否有调试器与日志;
  • 42 是平均、瞬时还是长帧后的显示。

Profiler 是证据采集工具,不是自动诊断答案。

一、本课目标 ​

Profiler 不是“找一个数字最大的函数”,而是回答:

text
哪一帧超预算?
超预算时 CPU 和 GPU 谁更忙?
时间集中在哪一类系统?
修改后是否真的改善?

二、采样前先建立可复现步骤 ​

text
启动应用
进入固定场景
执行固定操作
等待状态稳定
采样固定时长
保存数据和设备信息

不要边拖动场景边凭肉眼看曲线,然后把偶然结果当结论。


三、先看总览,再看细节 ​

推荐顺序:

text
Frame Time / FPS
    ↓
CPU / GPU 总时间
    ↓
脚本、渲染、布局、资源等类别
    ↓
具体函数或节点
    ↓
回到用户操作验证

如果总帧时间没有超预算,单个函数耗时较大也不一定是当前优先级最高的问题。


四、Profiler 中常见的判断 ​

现象初步假设
CPU 高、GPU 低脚本、遍历、布局、对象管理或提交准备
GPU 高、CPU 低Overdraw、Shader、纹理带宽或分辨率
每隔几秒尖峰GC、定时任务、资源加载、批量刷新
打开页面首帧慢反序列化、实例化、解码、首次渲染
场景后内存不降引用、缓存、监听器或资源释放

这些只是方向,不是最终结论。


五、采样环境会改变结果 ​

开发环境中可能存在:

  • 调试日志。
  • 编辑器额外开销。
  • 浏览器开发者工具。
  • Profiler 自身插桩成本。
  • 模拟器和真机差异。

结论应区分:

text
编辑器预览:定位逻辑
发布构建:验证真实性能
目标设备:确认用户体验

六、用标记缩小范围 ​

可以在关键操作前后记录时间或自定义标记:

ts
const start = performance.now();
this.refreshShop();
const cost = performance.now() - start;
cc.log('refreshShop', cost);

这类测量适合定位业务区间,不应在每帧高频输出。最终仍要结合整体帧时间判断。


七、先定位长帧,再定位平均耗时 ​

如果卡顿发生在某一帧:

text
选中长帧
→ 查看该帧的调用树
→ 找到突增的系统
→ 关联触发操作

如果是持续低帧:

text
观察稳定采样区间
→ 比较 CPU/GPU 平均负载
→ 找持续占用最高的类别

两种问题不能使用同一套优化策略。


八、常见误区 ​

误区一:Profiler 中最深的函数就是根因 ​

深层函数可能只是被频繁调用的结果,应该看调用次数、总耗时和触发条件。

误区二:编辑器中的 FPS 就是真机 FPS ​

编辑器、浏览器、模拟器和真机运行环境不同。

误区三:打开 Profiler 后数值没有变化 ​

工具可能改变调度和日志开销,需要做基准对比并保持采样方式一致。

误区四:只采样一次 ​

一次采样可能包含缓存、首次加载或偶然 GC,至少要重复验证。


九、练习与答案 ​

  1. 使用 Profiler 的第一步是什么?
  2. CPU 高、GPU 低通常优先检查哪些系统?
  3. 为什么要区分编辑器预览和发布构建?
  4. 如何分别排查持续低帧和偶发尖峰?

答案:

  1. 建立固定、可重复的操作和采样环境。
  2. 脚本、遍历、布局、对象创建和渲染提交准备。
  3. 两者有额外工具和平台开销,不能直接代表目标设备。
  4. 持续低帧看稳定区间总成本,尖峰看长帧调用树和触发事件。

一份可复现采样说明 ​

每次性能记录至少包含:

text
commit/build:
Creator 版本:
平台/设备/系统:
画质/分辨率/目标帧率:
网络与缓存状态:
测试账号和数据量:
操作步骤:
预热次数:
采样时长:
关注指标:

没有这些元数据,优化前后截图很难比较。

Creator 2.4.x 实验:建立三段时间线 ​

场景动作 ​

text
阶段 A:静置 5 秒
阶段 B:打开复杂页面
阶段 C:连续滚动 10 秒
阶段 D:关闭并等待 5 秒

PerfMarker.ts ​

ts
const { ccclass } = cc._decorator;

@ccclass
export default class PerfMarker extends cc.Component {
    mark(name: string) {
        cc.log(
            '[PERF-MARK]',
            name,
            'time=', Date.now()
        );
    }

    showStats() {
        cc.profiler.showStats();
    }

    hideStats() {
        cc.profiler.hideStats();
    }
}

通过按钮在每阶段开始/结束调用 mark。内置 Stats 用于现场观察,完整时间线结合浏览器或原生分析器。

实验步骤 ​

  1. 不开调试日志采一次基线。
  2. 开大量每帧日志再采一次,观察工具本身扰动。
  3. 冷缓存打开页面一次,热缓存再一次。
  4. 分别记录打开尖峰和滚动稳定段。
  5. 在相同动作下修改一个变量后复测。

从总览到调用栈的顺序 ​

text
先确认哪一段用户动作慢
→ 是单帧尖峰还是持续超预算
→ CPU/GPU/Memory/IO 哪个先可疑
→ 在对应工具中展开最长区间
→ 找到具体函数/资源/提交
→ 回到代码设计所有者

看到某函数“总耗时高”还要区分:

  • Self Time:函数自身;
  • Total Time:含子调用;
  • 调用次数;
  • 单次耗时;
  • 是否只在初始化发生;
  • 是否位于关键用户动作。

采样工具怎样改变结果 ​

  • DevTools 会增加开销;
  • 大量 console 日志会阻塞;
  • Native Debug 构建与 Release 不同;
  • USB/远程调试可能改变线程;
  • 第一次运行包含 JIT、缓存和 shader;
  • Profiler 自身有采样成本。

工具结果不是“假”,但必须在已知环境中解释,并用接近发布配置复验关键结论。

真实项目故障:优化了最显眼函数却没有改善 ​

Flame Chart 顶部显示 updateList 总耗时很高。团队优化其内部循环,帧时间几乎不变。进一步看发现大部分 Total Time 来自它触发的 Layout 和 Label 更新,自身 Self Time 很低。

正确修复:

text
updateList 调用频率降低
→ 只更新变化 Item
→ 固定 Label 尺寸
→ 合并 Layout 刷新
→ p99 明显下降

常见采样陷阱 ​

只截最差一帧 ​

可能是后台任务或偶发系统抖动。要能重复触发并看分布。

只测编辑器预览 ​

无法代表原生 JSB、纹理格式和 GPU。

修改后只测一次 ​

缓存、温度和随机负载可制造假改善。

只看 FPS ​

无法定位函数、资源、GPU 或内存。

版本边界与练习答案 ​

Creator 2.4.x 内置 Profiler 指标有限,Web 使用浏览器工具,Native 使用 Xcode Instruments、Android Profiler、系统 GPU 工具等。指标名称和口径以工具版本为准。

  1. 性能截图为什么必须带操作步骤? 指标只有映射到用户动作,才知道哪段时间应分析和回归。
  2. Self Time 与 Total Time 有何区别? Self 是函数自身,Total 包含其调用的子函数。
  3. 为什么冷/热缓存要分开? 冷路径包含读取、解码、编译或上传,热路径可能直接复用。
  4. 如何证明优化有效? 同设备、同构建条件、同数据和动作,多次采样比较目标指标与副作用。

十、本课总结 ​

text
Profiler 用来建立证据链,不是用来猜答案。
先看 Frame Time,再分 CPU/GPU,再下钻到系统和函数。
采样必须可复现,验证必须回到真实操作和目标设备。

十一、下一课预告 ​

text
Lesson058|CPU 性能:一帧时间到底花在哪里?