Unity3D 优化深入浅出 2-CPU 篇(公众号笔记)

ABSTRACT

2018-01 公众号笔记,优化系列第二篇。拆解 CPU 消耗的七大来源(业务逻辑、网络、I/O、GC、drawcall、物理、垂直同步),给出 Update 写法禁忌、GC 减垃圾清单、drawcall 预算公式与合批失效的常见坑。

核心要点

  • CPU 直接决定帧率、耗电量与发热量;消耗大头是业务逻辑、GC 回收与 drawcall。
  • Update 禁忌:不打印日志、不调用 GetComponent/SendMessage/FindWithTag、不声明临时变量、不做高频复杂运算(可隔 N 帧一次)。
  • 缓存引用收益巨大:GetComponent 约 619ms、MonoBehaviour 属性约 60ms、手动缓存引用约 3ms(实测数量级对比)。
  • 用 OnBecameVisible/OnBecameInvisible 控制不可见物体的 Update 执行;用 Vector3.zero 等内建值代替 new;大值类型参数用 ref 传递。
  • GC 减垃圾清单:字符串拼接用 StringBuilder;少用 foreach(迭代器产生约 24B 垃圾)改用 for;用 CompareTag 代替访问 tag 属性;对象池代替 Instantiate/Destroy;小结构用 struct;避免 LINQ(分配临时空间且部分泛型在 AOT 下无法编译,iOS 会报错);场景切换时主动 GC。
  • 网络下载可多线程,但主线程加载大模型/大纹理要用队列控制,避免同帧 Camera.Render 渲染过多三角形造成卡顿。
  • drawcall 预算公式(NVIDIA GDC 数据):25K batchs/sec 吃满 1GHz CPU;DrawCall_Num = 25K × CPU频率 × CPU分配百分比 / 目标帧率。
  • 合批常见失效点:多通道 shader 无法动态批处理;多盏实时灯打断静态批处理;带 lightmap 的物体因隐藏材质属性无法合批;接收实时阴影的物体不合批。
  • 同 shader 不同纹理导致的材质分裂,可通过纹理合并(图集)使其可被静态批处理。
  • 游戏引擎避免多线程的根因:主循环结构要求逻辑与画面更新时间点确定,引入多线程需同步,大幅增加复杂度。

关键实体与概念

Update/LateUpdate、GC、StringBuilder、CompareTag、对象池、AOT 编译、LINQ、DrawCall 预算、Dynamic/Static Batching、lightmap、VSync

关联概念

来源回溯

  • 原始文件:raw/ip/wechat_articles/Unity3D优化深入浅出2-CPU篇.md(2018-01-11)

时效性评估

  • 仍有效:缓存组件引用、减少 GC 分配、对象池、字符串拼接优化等是 C#/Unity 长青实践;drawcall 预算思路与合批失效原理依然适用。
  • 已过时:Unity 5 时代的 Stats/Profiler 界面与参数已变化;现代 Unity(2020+)有 SRP Batcher、Burst 编译器、Job System,多线程禁忌已被官方并发方案部分打破;增量式 GC 缓解了 GC 卡顿。CompareTag、foreach 在 IL2CPP 下开销已大幅改善,但习惯仍值得保留。