Skip to content

Stage05|Performance ​

Lesson066|load、preload、缓存与加载峰值 ​

Tags: #Creator2.x #Stage05 #Load #Preload #Cache #MemoryDifficulty: ⭐⭐⭐⭐☆


真实问题:预加载以后,打开页面为什么仍然内存峰值 ​

项目预加载了 Prefab 和图集,打开页面时仍发生峰值:

text
缓存中的压缩/Asset 数据
+ instantiate 产生 Node/Component
+ Texture 首次上传
+ Label 字形
+ Layout 与 RenderData
→ 打开峰值

preload 只能提前部分加载工作,不会自动完成页面构建和渲染预热。

一、本课目标 ​

资源加载优化不是单纯“提前全部加载”。本课区分:

  • 正式加载和预加载。
  • 缓存命中和首次加载。
  • 加载时间、首屏时间和内存峰值。
  • 预加载过多为什么也会拖慢启动。

二、load 和 preload 的语义 ​

在 Creator 2.4.x Asset Manager 中,两者不是同一条流程的两个名字:

text
preload:提前下载资源及必要依赖,不做完整反序列化和初始化
load:在业务需要时继续解析、初始化并返回可用 Asset

preload 的目标主要是把网络或文件读取等待前移。它会消耗网络、IO 和下载缓存空间,但不能据此断言 Prefab 对象、解码纹理或 GPU 资源已经常驻内存。后续仍需调用 load。

官方版本依据:Asset Manager|Loading and Preloading。


三、资源加载的成本链路 ​

text
preload 路径:
定位资源 → 读取或下载 → 写入下载缓存

后续 load 路径:
读取下载结果 → 解析 / 解码 → 反序列化
→ 创建运行时 Asset → 在实际使用阶段准备渲染资源

不同资源的成本分布不同。图片可能卡在解码和首次 GPU 使用,Prefab 可能卡在反序列化和后续实例化,远程资源还多了网络等待。测量时必须标记当前观察的是 preload、load、instantiate 还是首次渲染。


四、缓存命中为什么更快 ​

text
第一次请求
→ 读取、解析、创建
→ 放入缓存

第二次请求
→ 找到缓存对象或依赖
→ 减少重复 IO 和解析

缓存提高速度,但也会延长资源生命周期。性能优化必须同时观察加载时间和内存占用。


五、加载峰值 ​

场景切换时可能出现:

text
旧场景资源仍在
新场景资源开始加载
临时解码数据存在
新纹理等待上传

这段重叠时间就是峰值风险。可以采用:

  • 分批加载。
  • 控制并发。
  • 提前释放确定不用的旧资源。
  • 先加载首屏必须资源。
  • 其余内容延迟到页面稳定后加载。

六、首屏时间和总加载时间 ​

用户关心的是:

text
点击后多久看到可操作内容?

不是所有资源都必须在首屏前加载。可以分层:

text
首屏必需
→ 首屏可交互
→ 首屏后增强
→ 用户触发时再加载

把所有资源放入启动阶段,会增加首屏时间和峰值。


七、加载竞态 ​

text
用户进入页面 A
→ 开始加载 A
→ 用户快速进入页面 B
→ A 加载完成回调晚到

如果没有请求令牌,A 的回调可能修改 B 页面。处理方法:

  • 保存请求 ID。
  • 页面退出时取消或标记失效。
  • 回调检查当前页面和对象有效性。
  • 只让当前请求提交结果。

八、案例:商城分层加载 ​

text
进入商城
→ 加载背景、导航和首屏 Item
→ 页面可交互
→ 预加载下一页图标
→ 用户滚动时补充详情资源

这样比一次加载所有商品图片更容易控制首屏和峰值。


九、常见误区 ​

误区一:preload 越多越好 ​

预加载会提前占用网络、IO 和下载缓存,并可能和其他下载任务争抢并发;完整解析和运行时资源内存通常仍留在后续 load 阶段。

误区二:缓存命中就没有成本 ​

仍可能有引用、校验、对象创建和 GPU 使用成本。

误区三:加载完成回调一定对应当前页面 ​

异步竞态会让旧请求晚于页面切换返回。

误区四:只看加载时间,不看峰值 ​

压低等待时间但提高内存峰值,可能在低端设备上更危险。


十、练习与答案 ​

  1. preload 解决什么问题,又带来什么代价?
  2. 为什么首屏资源要分层?
  3. 如何防止旧页面加载回调修改新页面?
  4. 场景切换时为什么容易出现内存峰值?

答案:

  1. 主要把下载等待前移;代价是提前消耗网络、IO、并发额度和下载缓存,后续 load 成本仍需测量。
  2. 让用户先可操作,再延迟非关键资源。
  3. 使用请求令牌、生命周期检查或取消机制。
  4. 新旧资源和临时解码数据在一段时间内重叠。

加载峰值来自“新旧同时存在” ​

场景 A 切 B:

text
A 的 Node/Texture 尚未释放
→ B 开始读取
→ B 解码缓冲
→ B Texture 上传
→ B instantiate
→ 峰值
→ A 最终归还

总加载量不变,改变先后顺序就能显著改变峰值,但可能影响转场体验。

Creator 2.4.x 实验:分阶段打开页面 ​

LoadPeakProbe.ts ​

ts
const { ccclass, property } = cc._decorator;

@ccclass
export default class LoadPeakProbe extends cc.Component {
    @property(cc.Node)
    container: cc.Node = null;

    private generation = 0;
    private prefab: cc.Prefab = null;

    preload() {
        cc.resources.preload(
            'perf-lab/heavy-panel',
            cc.Prefab,
            (err) => cc.log('[load-peak] preload', !!err)
        );
    }

    load() {
        const id = ++this.generation;
        const begin = Date.now();
        cc.resources.load(
            'perf-lab/heavy-panel',
            cc.Prefab,
            (err, prefab: cc.Prefab) => {
                cc.log('[load-peak] load ms=', Date.now() - begin);
                if (err || id !== this.generation) {
                    return;
                }
                this.prefab = prefab;
                this.buildAcrossFrames(id, 0);
            }
        );
    }

    private buildAcrossFrames(id: number, count: number) {
        if (id !== this.generation || !this.prefab) {
            return;
        }
        for (let i = 0; i < 5; i++) {
            this.container.addChild(cc.instantiate(this.prefab));
        }
        if (count < 20) {
            this.scheduleOnce(
                () => this.buildAcrossFrames(id, count + 1),
                0
            );
        }
    }

    cancel() {
        this.generation++;
    }
}

对照 ​

  1. 不 preload,直接 load+一次创建 100 个。
  2. preload 后再 load+一次创建。
  3. preload 后分帧创建。
  4. 每种记录 load callback、首屏可用、全部完成、p99 与内存峰值。

分帧可降低单帧尖峰,但总完成时间可能更长,应定义首屏优先级。

缓存命中不是免费 ​

即使 Asset 已缓存,仍可能有:

  • instantiate;
  • Component 生命周期;
  • 数据绑定;
  • Layout;
  • GPU 首次使用;
  • 大量对象一次性加入树;
  • JS→Native 提交。

优化报告应拆开:

text
resource ready
first usable UI
all content ready

真实项目故障:商城预加载拖慢战斗 ​

为了商城秒开,游戏启动后立即后台预加载全部商城图集。玩家直接进入战斗时,商城 IO/解码与战斗资源争用,造成战斗开场卡顿和内存峰值。

修复:

  • 按用户路径与优先级调度;
  • 战斗关键加载完成后再后台;
  • 限制并发和单帧任务;
  • 可取消/降优先级的预加载;
  • 内存不足时不保留低价值缓存;
  • 测“预加载对当前页面”的干扰。

版本边界与练习答案 ​

Creator 2.4.x preload/load 的具体准备边界需按 Asset 类型和平台验证,不能假设 preload 已完成 GPU 上传。

  1. preload 与 load 的目标差异是什么? preload 提前准备依赖/数据,load 正式取得 Asset 供业务使用;具体边界按版本。
  2. 分帧创建的代价是什么? 总完成时间可能变长,状态管理更复杂,需提供渐进 UI。
  3. 缓存越多越好吗? 命中更快但内存更高,并可能干扰当前关键路径。
  4. 如何验收加载优化? 同时看冷/热路径、首屏可用时间、全部完成、p99、峰值内存和取消正确性。

十一、本课总结 ​

text
加载优化要同时看等待时间、首屏时间、缓存命中和内存峰值。
预加载应服务于体验,而不是把所有资源提前搬进内存。

十二、下一课预告 ​

text
Lesson067|资源释放、场景切换与内存不下降