外观
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,至少要重复验证。
九、练习与答案
- 使用 Profiler 的第一步是什么?
- CPU 高、GPU 低通常优先检查哪些系统?
- 为什么要区分编辑器预览和发布构建?
- 如何分别排查持续低帧和偶发尖峰?
答案:
- 建立固定、可重复的操作和采样环境。
- 脚本、遍历、布局、对象创建和渲染提交准备。
- 两者有额外工具和平台开销,不能直接代表目标设备。
- 持续低帧看稳定区间总成本,尖峰看长帧调用树和触发事件。
一份可复现采样说明
每次性能记录至少包含:
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 用于现场观察,完整时间线结合浏览器或原生分析器。
实验步骤
- 不开调试日志采一次基线。
- 开大量每帧日志再采一次,观察工具本身扰动。
- 冷缓存打开页面一次,热缓存再一次。
- 分别记录打开尖峰和滚动稳定段。
- 在相同动作下修改一个变量后复测。
从总览到调用栈的顺序
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 工具等。指标名称和口径以工具版本为准。
- 性能截图为什么必须带操作步骤? 指标只有映射到用户动作,才知道哪段时间应分析和回归。
- Self Time 与 Total Time 有何区别? Self 是函数自身,Total 包含其调用的子函数。
- 为什么冷/热缓存要分开? 冷路径包含读取、解码、编译或上传,热路径可能直接复用。
- 如何证明优化有效? 同设备、同构建条件、同数据和动作,多次采样比较目标指标与副作用。
十、本课总结
text
Profiler 用来建立证据链,不是用来猜答案。
先看 Frame Time,再分 CPU/GPU,再下钻到系统和函数。
采样必须可复现,验证必须回到真实操作和目标设备。十一、下一课预告
text
Lesson058|CPU 性能:一帧时间到底花在哪里?