外观
Stage05|Performance
Lesson072|阶段复习:建立可复现、可量化的性能证据链
Tags: #Creator2.x #Stage05 #Performance #Profiler #OptimizationDifficulty: ⭐⭐⭐⭐☆
阶段挑战:把“感觉卡”变成可证伪报告
性能工程不是收集优化口诀,而是把用户症状转换成:
text
可复现动作
→ 时间线与指标
→ 瓶颈假设
→ 单变量实验
→ 修改与回归
→ 目标设备结论本课用一页“活动商城”验收 Stage05 的全部能力。
一、本课目标
串联 Lesson055~Lesson071,完成 Performance 阶段闭环。
本阶段的核心问题是:
面对“为什么卡”,如何从现象建立一条能复现、能测量、能验证的证据链?
二、性能知识树
text
四个维度
├── CPU
├── GPU
├── Memory
└── IO
分析工具
├── Frame Time / FPS
├── Profiler
├── 内存和资源统计
└── 真机平台工具
常见系统
├── Node / Component
├── Layout / ScrollView
├── Label / Animation / Particle
├── GC / ObjectPool
└── Load / Release / Cache三、性能排查标准流程
text
稳定复现
↓
记录设备、版本、数据量和操作
↓
测量 Frame Time、CPU、GPU、Memory、IO
↓
判断持续超预算还是偶发尖峰
↓
找到最大成本和触发条件
↓
提出单一假设
↓
修改一个变量
↓
回归测试和记录指标四、从 API 到性能问题
修改 UI
text
label.string
→ 文本和 RenderData 更新
→ Layout 传播
→ DrawCall / Texture
→ CPU / GPU 成本创建特效
text
instantiate
→ 生命周期和调度
→ Particle / Sprite
→ GC、DrawCall、Overdraw
→ destroy 或对象池回收打开场景
text
loadScene
→ IO / 解码 / 反序列化
→ Node / Component 创建
→ 首次布局和渲染
→ 内存峰值性能分析的价值就是沿着这些链路找到实际成本。
五、阶段综合练习
问题:
text
一个活动页面首屏打开慢,滑动时偶尔卡顿,重复进入后内存持续升高。请按以下格式提交分析:
text
现象:
复现步骤:
设备和版本:
指标:
初步维度判断:
可能根因:
单变量实验:
优化结果:
残余风险:参考思路:
text
打开慢 → 资源加载、解码、实例化、首屏布局
滑动卡 → ScrollView、Layout、Label、Overdraw
内存升高 → 事件、异步回调、缓存、Texture、对象池六、Stage05 面试题
1. FPS 和 Frame Time 的区别是什么?
FPS 是每秒帧数,Frame Time 是完成一帧的时间。性能排查通常更关心帧时间分布和尖峰。
2. DrawCall 少为什么仍可能卡?
Overdraw、Shader、纹理带宽、分辨率或 CPU 其他工作仍可能超预算。
3. destroy 节点为什么不等于释放资源?
资源可能被 SpriteFrame、其他节点、缓存、Bundle 或 GPU 继续引用。
4. 对象池的风险是什么?
状态重置遗漏、事件和 Tween 残留、池无限增长以及长期内存占用。
5. 如何排查内存泄漏?
重复进入和退出场景,观察趋势,检查事件、异步回调、全局引用、缓存、资源和对象池。
6. 为什么优化前后要单变量验证?
否则无法判断哪个修改造成收益或回归。
七、本阶段最终能力
完成 Stage05 后,你应该能够:
- 用 Frame Time 而不是只用 FPS 描述卡顿。
- 先判断 CPU、GPU、Memory、IO 维度。
- 使用 Profiler 选择长帧和稳定区间。
- 解释 Node、Layout、Label、Particle、GC 和对象池成本。
- 设计加载、缓存、释放和内存峰值方案。
- 写出可复现、可量化、可回归的性能报告。
最终记住:
高级优化不是“知道更多优化技巧”,而是能用证据证明问题、修改和结果之间的因果关系。
阶段综合实验:Performance Evidence Lab
场景、资源、Profiler 标记和测试数据通过 Creator 2.4.x 工作流建立。不修改生成目录或序列化资源文本。
负载开关
准备以下可独立启用的模块:
text
NodeLoad:2000 节点/组件
LayoutLoad:500 条自适应 Item
LabelLoad:高频动态文字
PoolLoad:弹幕/伤害数字
LoadPeak:大图 + Prefab 加载
GpuLoad:全屏透明层/粒子
LeakLoad:故意保留引用(仅实验)EvidenceLab.ts
ts
const { ccclass, property } = cc._decorator;
@ccclass
export default class EvidenceLab extends cc.Component {
@property([cc.Node])
modules: cc.Node[] = [];
private samples: number[] = [];
private begin = 0;
startRun() {
this.begin = Date.now();
this.samples.length = 0;
cc.log('[evidence] begin');
}
mark(name: string) {
cc.log(
'[evidence]',
name,
Date.now() - this.begin
);
}
enableOnly(index: number) {
for (let i = 0; i < this.modules.length; i++) {
this.modules[i].active = i === index;
}
}
update(dt: number) {
this.samples.push(dt * 1000);
if (this.samples.length > 600) {
this.samples.shift();
}
}
report() {
const values = this.samples.slice().sort((a, b) => a - b);
if (!values.length) {
return;
}
const at = (ratio: number) =>
values[Math.floor((values.length - 1) * ratio)];
cc.log(
'[evidence]',
'p50=', at(0.5),
'p95=', at(0.95),
'p99=', at(0.99),
'max=', values[values.length - 1]
);
}
}验收流程
- 固定设备、构建、数据和动作。
- 只启用一个模块,确认指标变化。
- 组合 Node/Label/Layout,记录 CPU 与 p99。
- 组合 Texture/LoadPeak/Gpu,记录 IO、内存和 GPU。
- 运行 LeakLoad 十次循环,确认故意增长能被证据发现。
- 对每个改动做回归和失败路径测试。
性能报告模板
text
症状:
用户动作:
设备/构建:
数据规模:
基线:
主要指标:
假设:
实验变量:
结果:
修改:
副作用:
回归矩阵:
结论与未解决风险:没有“未解决风险”的报告通常意味着没有诚实记录边界。
Stage05 故障树
text
首屏慢
├── IO/解码/上传
├── 一次性 instantiate
├── Label/Layout/字形
├── Shader/纹理预热
└── 并发预加载峰值
持续低帧
├── CPU update/遍历/布局
├── GC/高频分配
├── DrawCall/状态提交
├── GPU Overdraw/FillRate
└── 分辨率/热降频
越进越慢
├── 监听/定时器闭包
├── 缓存无淘汰
├── 对象池无上限
├── Texture/Asset 引用
└── 场景循环残留先按症状选择证据,再按证据选修复。
阶段真实案例:性能修复互相抵消
团队同时:
- 把资源移入 Bundle;
- 增大对象池;
- 开 Dynamic Atlas;
- 关闭部分 Label;
- 加后台预加载。
最终首屏变快但内存和滚动变差,无法知道哪项造成副作用。
正确流程是拆分提交或保留 feature flag,逐项测:
text
基线
→ A 资源拆分
→ B 对象池
→ C 动态合图
→ D Label 降频
→ E 后台预加载
→ 组合方案组合方案仍需重新测,因为非线性相互作用可能出现。
Stage05 能力验收
你应该能够:
- 区分 CPU、GPU、Memory、IO 症状与证据。
- 用 Frame Time 分位数描述长帧。
- 在 Creator Profiler 和平台工具中建立可复现采样。
- 计算图片/Atlas 的内存数量级并设计预算。
- 解释 Node、Layout、Label、GC、Pool 的不同成本。
- 设计 load/preload 的首屏与峰值策略。
- 为监听器、缓存、池和资源写所有权/淘汰点。
- 在微信小游戏目标上做包体、内存、渲染和网络回归。
- 产出带基线、假设、实验和副作用的性能报告。
综合练习与答案
练习一
页面平均 58 FPS,p99 75 ms;关闭全屏半透明层后 p99 22 ms,CPU几乎不变。结论是什么?
答案: 有强 GPU/Overdraw 证据;需继续测覆盖面积、Shader/RenderTexture,并在目标设备确认,不要只凭一次开关结论。
练习二
场景循环后 JS Heap 稳定但 Texture Memory 每轮上涨。优先查什么?
答案: 动态 Texture/Atlas 的缓存、addRef/decRef、SpriteFrame/对象池和随机缓存键;JS 堆正常不能排除原生/GPU引用。
练习三
对象池把创建尖峰降为零,但内存超预算。方案是什么?
答案: 记录并发峰值,缩小池容量/分层池化/动态缩容,简化 Item,清理资源引用;不能回到无限预热。
练习四
后台预加载让首屏偶尔长帧,如何验证?
答案: 在相同冷缓存设备关闭后台任务做 A/B,标记 IO/解码/上传和 p99;限制并发或延后到首屏稳定后,比较首屏、全完成、峰值和滚动。
版本边界与验证入口
Stage05 的数值必须绑定设备、构建和平台;Creator 2.4.x 内置工具与 Web/Native/小游戏平台工具不可混为同一口径。
八、下一阶段预告
Stage05 完成,下一阶段进入:
text
Stage06|JSB & Native Bridge
Lesson073|JavaScript Engine、JSB 与 Cocos2d-x 的关系下一阶段会把性能和运行时继续向下追踪:
text
JavaScript
→ JavaScript Engine
→ JSB
→ C++ Engine
→ Native Platform