外观
Stage05|Performance
Lesson055|性能问题的四个维度:CPU、GPU、Memory、IO
Tags: #Creator2.x #Stage05 #Performance #CPU #GPU #Memory #IODifficulty: ⭐⭐⭐☆☆
真实问题:玩家说“商城很卡”,你第一步改什么
错误回答通常是“减少节点”“压缩图片”或“上对象池”。但“卡”可能指:
text
打开时停顿 800 ms → IO / 解码 / 创建峰值
滚动持续低帧率 → CPU / GPU
反复进出后越来越慢 → Memory / 泄漏 / GC
首次显示图片时抖一下 → 上传 / shader / 字形预热没有时间线和指标,任何优化都只是猜。
一、本课目标
性能优化不是看到“卡”就随意减少节点或 DrawCall,而是先回答:
这一帧究竟被哪一种资源限制住了?
本课建立四个维度:
text
CPU:脚本、引擎更新、布局和提交
GPU:顶点、像素、带宽和渲染状态
Memory:内存、显存、GC 和峰值
IO:资源读取、解码、网络和磁盘二、为什么“卡”不是一个答案
下面几种问题都可能被用户描述为卡顿:
| 表现 | 可能的根因 |
|---|---|
| 持续低帧 | CPU 或 GPU 长期超预算 |
| 偶发尖峰 | GC、资源加载、着色器编译、同步等待 |
| 页面打开慢 | IO、反序列化、纹理解码、首次渲染 |
| 滑动掉帧 | Layout、节点更新、Fill Rate 或输入处理 |
| 内存越玩越高 | 引用、缓存、监听器或资源释放问题 |
第一步永远是把现象变成指标:
text
何时发生?
哪台设备?
持续多久?
平均值还是尖峰?
CPU、GPU、内存和 IO 哪个同时异常?三、CPU 瓶颈
CPU 可能花在:
- Component
update和lateUpdate。 - Node 遍历、Transform 和激活状态传播。
- Widget、Layout、ScrollView 刷新。
- Label、Animation、Particle 数据更新。
- 事件分发、定时器和对象创建。
- 组装渲染数据和提交 DrawCall。
CPU 瓶颈的典型特征是:
text
主线程帧时间很长
GPU 仍有空闲
降低分辨率后改善不明显四、GPU 瓶颈
GPU 可能花在:
- 顶点数量。
- Fragment Shader 复杂度。
- Overdraw 和 Fill Rate。
- 纹理带宽。
- Mask、Stencil、Blend 和后处理。
- 屏幕分辨率过高。
典型特征:
text
降低分辨率或关闭特效后明显改善
CPU 主线程并不满
复杂半透明 UI 区域最严重五、Memory 瓶颈
内存问题包括:
text
JavaScript Heap
Native Heap
Texture Memory
Audio / Spine / Particle 数据
缓存和临时对象要区分:
- 当前占用高但稳定。
- 场景切换后无法下降。
- 峰值超过平台限制导致崩溃。
- GC 频繁导致帧时间尖峰。
“内存高”不一定等于“泄漏”,需要观察生命周期和趋势。
六、IO 瓶颈
IO 不只指磁盘:
text
本地文件读取
网络下载
资源解压
图片 / 音频解码
Asset Bundle 加载
纹理上传前的数据准备首屏慢常见于 IO 和 CPU 同时工作。资源压缩可能减少下载量,却增加解压时间;降低图片尺寸可能减少显存,却需要重新导入和上传。
七、不要用错优化方向
text
CPU 瓶颈 → 只压缩图片
GPU 瓶颈 → 只删 update
内存泄漏 → 只降低纹理分辨率
加载慢 → 只减少节点数量这些做法可能有效,但没有证据时属于猜测。正确流程是:
text
复现
→ 采样
→ 定位维度
→ 找最大成本
→ 修改
→ 重新采样八、案例:商城首页打开卡顿
可能链路:
text
加载大量图片
→ 解码
→ 创建 SpriteFrame
→ 创建列表 Item
→ Layout 重排
→ 首次上传纹理
→ 大量半透明 UI 绘制应分别记录:
- 资源下载和读取时间。
- 反序列化和实例化时间。
- 首次布局耗时。
- 首帧 CPU/GPU 时间。
- 图片内存和纹理显存峰值。
九、常见误区
误区一:DrawCall 越低越好
DrawCall 只是 GPU/CPU 提交成本的一部分,Overdraw、Shader 和纹理带宽仍可能成为瓶颈。
误区二:内存高就是泄漏
缓存、预加载和平台保留都可能让占用暂时偏高,要看是否持续增长以及引用链。
误区三:删节点能解决所有性能问题
如果瓶颈是 GPU Fill Rate、IO 或纹理显存,删空节点可能没有明显收益。
误区四:优化前后只比较 FPS
还要比较帧时间分布、内存峰值、加载时间和设备条件。
十、练习与答案
练习
- 如何区分 CPU 瓶颈和 GPU 瓶颈?
- 为什么场景切换慢可能同时属于 IO 和 CPU 问题?
- 内存高和内存泄漏有什么区别?
- 性能优化为什么必须记录设备和复现步骤?
参考答案
- 观察主线程时间、GPU 时间、分辨率变化后的结果和 Profiler 数据。
- 资源读取/解码属于 IO,反序列化/创建/布局属于 CPU。
- 内存高可能稳定,泄漏通常表现为生命周期结束后仍持续增长或对象无法回收。
- 不同设备、分辨率和平台的瓶颈不同,没有环境就无法比较结论。
性能调查的标准闭环
text
定义用户动作
→ 固定设备、版本与数据
→ 采集基线
→ 判断 CPU/GPU/Memory/IO 主嫌疑
→ 提出一个可证伪假设
→ 只改一个核心变量
→ 同条件复测
→ 检查视觉、功能和副作用
→ 记录结论“可证伪”意味着实验结果可能推翻假设。例如:
假设滚动慢主要由 GPU Overdraw 导致;把全屏透明层关闭后,GPU Frame 应显著下降,而 CPU 基本不变。
如果结果不符合,就换方向,不为原猜测找借口。
Creator 2.4.x 编辑器实验:四类压力开关
实验资源和节点通过 Creator 编辑器创建,不修改生成目录。内存与 IO 实验在测试场景中进行,不放进正式业务。
PerformanceDimensionLab.ts
ts
const { ccclass, property } = cc._decorator;
@ccclass
export default class PerformanceDimensionLab extends cc.Component {
@property(cc.Node)
overdrawLayer: cc.Node = null;
private cpuEnabled = false;
private retained: number[][] = [];
toggleCpu() {
this.cpuEnabled = !this.cpuEnabled;
}
toggleGpu() {
this.overdrawLayer.active = !this.overdrawLayer.active;
}
allocateMemory() {
for (let i = 0; i < 2000; i++) {
this.retained.push(new Array(100).fill(i));
}
cc.log('[perf-lab] retained=', this.retained.length);
}
clearMemory() {
this.retained.length = 0;
}
loadResource() {
const begin = Date.now();
cc.resources.load(
'perf-lab/large-image',
cc.SpriteFrame,
(err) => {
cc.log(
'[perf-lab] load ms=',
Date.now() - begin,
'error=', !!err
);
}
);
}
update() {
if (!this.cpuEnabled) {
return;
}
let value = 0;
for (let i = 0; i < 200000; i++) {
value += Math.sqrt(i);
}
if (value < 0) {
cc.log(value);
}
}
}OverdrawLayer 使用多层全屏透明 Sprite。large-image 使用合法测试资源。
观察
- CPU 开关主要改变脚本帧时间。
- Overdraw 开关在低端/高分辨率设备更可能改变 GPU。
- allocate 使 JS 堆增加,但 clear 后不保证进程内存立即归零。
- 首次 load 与第二次 load 可能因缓存呈现不同 IO/解码时序。
实验是分类器,不是模拟真实业务的最终结论。
四个维度如何互相影响
text
IO 读取大图
→ CPU 解码
→ Memory 峰值
→ GPU 上传
→ 首帧卡顿因此性能瓶颈不是永远单选题。应找当前用户症状的主导阶段,并记录次要风险。
真实项目事故:先压图后页面反而更慢
团队为了包体把图片压得更小,但目标平台运行时仍解码为大纹理,且新格式解码成本更高。结果包体下降,打开峰值变慢,纹理内存基本不变。
正确验收至少包含:
- 下载/包体;
- 冷加载时间;
- 解码与首帧;
- Texture Memory;
- 画质;
- 目标平台兼容。
诊断矩阵
| 现象 | 优先证据 | 常见误判 |
|---|---|---|
| 单帧尖峰 | 帧时间时间线、加载/GC/创建日志 | 只看平均 FPS |
| 持续低帧 | CPU/GPU 对照、分辨率实验 | 先清缓存 |
| 内存阶梯涨 | 多轮稳定平台、对象/纹理计数 | 一次关闭没降就叫泄漏 |
| 首次慢后续快 | 冷热缓存、上传/字形/Shader | 认为网络一定慢 |
版本边界与练习答案
Creator 2.4.x 内置 Profiler 提供总览,深入 CPU/GPU/Memory 仍需 Chrome DevTools、原生平台工具和目标设备。不同工具指标口径必须记录。
- “FPS 低”能否判断 CPU 或 GPU? 不能,需要 CPU/GPU 对照、降分辨率、禁用逻辑等实验。
- 为什么 Memory 也会造成卡顿? GC、分配器、换页、系统内存压力和资源重载都可能产生尖峰。
- 为什么 IO 优化要测冷缓存? 热缓存可能绕开磁盘/网络,掩盖首次用户体验。
- 一次只改一个变量有什么价值? 能把指标变化归因到具体修改,避免多个改动相互抵消。
十一、本课总结
text
先判断瓶颈维度,再选择优化手段。
CPU、GPU、Memory、IO 可能同时出现,但必须找最大成本。
性能结论必须绑定设备、场景、指标和验证方式。十二、下一课预告
text
Lesson056|FPS、Frame Time、刷新率与帧预算