性能优化-PR全流程
流程速览
- 工具层:Unity(Profiler / Frame Debugger / Memory Profiler / Overdraw)+ 平台工具(如 Xcode/Metal)+ 脚本侧(Lua Profiler)+ 项目级回归(UPR/自动化报告)
- 指标层:内存 / CPU / GPU(按“要采集 → 常见信号 → 常用抓手”组织)
- 流程层:复现 → 基线 → 定位 → 方案 → 实现 → 对比 → 回归 → PR 报告 → 监控与回滚
工具汇总
unity 内部工具使用
Profiler
Profiler是我们排查性能问题最常用的工具,可帮我们排查性能问题原因 Profiler使用教程:https://zhanglan.pages.dev/blog/unity_profiler_usage
FrameDebugger
Frame Debugger 主要用于排查 渲染顺序、Pass/Draw Call 组成、合批情况、材质/关键字切换 等问题。
常见情况:
- 版本打到真机上,有渲染粉色(Shader 丢失/变体缺失/材质引用异常)
- 调试游戏对象渲染 Shader 的变量值 FrameDebugger使用教程:https://zhanglan.pages.dev/blog/unity-frame-debugger
Memory Profiler
用于查看 内存占用大头(Top Allocations)、定位 未释放引用链、排查 内存泄漏/驻留不回落。
Memory Profiler使用教程 :https://zhanglan.pages.dev/blog/unity_memory_profiler
OverDraw
主要查看渲染相关的 屏幕填充率 以及 重复绘制率(尤其是 UI、透明特效、雾/云等)。 OverDraw使用教程:https://zhanglan.pages.dev/blog/rendering-debug-overdraw
Xcode
主要用于定位 移动端 GPU 瓶颈(如 GPU Frame Capture / Metal System Trace),辅助判断:
- 主要渲染阶段开销(Tile/Fragment/Compute 等)
- Render Pass / 纹理带宽 / 过度绘制(Overdraw)倾向
- 分辨率、后处理、透明叠加带来的 Fillrate Pressure
SLua
用于定位 Lua 脚本侧的 CPU 热点、内存分配、调用栈 等.
SLua Profiler使用教程: https://github.com/Tencent/sluaunreal/wiki/slua-Profiler-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E/aaee3a8e742aff3be3aa8602f2739e11f5295a8e
可用排查Lua的性能问题
UPR
UPR 可理解为“项目级性能体检与回归系统”:
- 统一跑固定 Case(场景/战斗/主城/加载)
- 采集核心指标(FPS、CPU/GPU Frame、内存峰值/回落、GC、加载尖刺)
- 生成对比报告(版本间/分支间),用于快速发现回归点并定位责任模块
性能优化流程
注意事项(保证数据可信)
- 固定变量:同一机型、同一画质档、同一分辨率/渲染比例、同一场景与操作路径、同一构建配置(尽量统一 Development/Release 口径)。
- 采样规范:
- 尖刺问题:至少抓到 尖刺前后 3–5 秒 的 Timeline(必要时加自定义 Marker/埋点)。
- 稳态问题:持续运行 30–120 秒,避免“偶然采样误判”。
- 每个 Case 建议 跑 3 次,取均值与波动范围。
- 设备覆盖:高/中/低端机都要跑。机型选择建议以线上监控后台 Top 机型为准。
- 对比方式:优化前后必须给出 Before/After 数据与截图证据(Profiler/Memory Profiler/Frame Debugger/Xcode 等)。
测试频率建议(工程化节奏)
- 新功能/大活动上线前:对新增模块做专项性能测试(重点关注 CPU/GPU 峰值与内存驻留)。
- 底层架构/核心链路改动:必须做针对性测试(代码效率、GC、资源驻留、加载尖刺)。
流程
上述只是列举了我们常用的性能优化工具,接下来我大概叙述一下我在做性能优化的大概流程,不展开详细的内容分析。
对于新的功能模块(Feature/专项优化 PR)
- 复现问题:给出可复现路径(机型/画质档/场景/操作步骤/出现概率)。
- 建立基线:抓一份“问题版本”的 Profiler/Memory/GPU 证据。
- 定位瓶颈归属:
- Profiler:确认是否 CPU 超预算、热点函数与调用链
- Memory Profiler:确认是否驻留不回落/泄漏/大头资源
- GPU:在高/中/低端机跑一遍,判断是否 GPU 超预算;辅助查看 Overdraw、Fillrate
- 提出方案与取舍:给出“为什么选这个方案”的理由与风险点。
- 实现与验证:同 Case 同口径抓 After 数据,对比收益与副作用。
- 回归验证:覆盖关键场景(登录/切场景/战斗/主城/长时间挂机/反复进出)。
- 输出 PR 报告:把证据链与结论固化,便于评审与后续追溯。
整个项目性能检测(版本体检/回归)
主要用 UPR(或等价的自动化/半自动化流程)跑整体性能,通过报告发现回归点并对症下药:
- 定位“版本差异最大的 Case/指标”
- 回到对应模块用 Profiler/Memory/GPU 工具补齐证据链
- 形成“回归清单 + 责任模块 + 修复计划”
性能模块汇总
内存
音频/声音
要采集:AudioClip 总量、长音频是否流式、解码/格式、切换场景是否回收。
常见信号:
- 切场景/账号切换后音频资源不回落
- 大量 stereo、采样率过高、同时播放过多
常用抓手:
- Force To Mono(适用时)、降低采样率、合理 Compression
- 长音频 Streaming,短音效 Decompress on load(按场景权衡)
- 音频预加载/池化,避免频繁加载卸载尖刺
网格(Mesh)
要采集:Mesh 数量、总顶点/三角形、重复网格、SkinnedMesh 占比。
常见信号:
- 主城/大世界峰值 Mesh 飙升、重复驻留
- 过重 SkinnedMesh 引发 CPU/GPU 双高
常用抓手:
- LOD / Impostor(远景)、合并策略(谨慎,避免“超大 Mesh”)
- 共享 Mesh / 减少重复实例化
纹理(Texture)
要采集:纹理总内存、Top 纹理、是否 Mipmap、压缩格式、是否重复驻留。
常见信号:
- 峰值不回落(切换后重复纹理残留)
- UI/特效大图导致峰值异常
常用抓手:
- 纹理压缩、分辨率分级(低端机降档)
- 图集/Texture2DArray(适用时)、减少重复贴图
- 检查 Read/Write Enabled、MipMap 使用是否合理
动画(Animation)
要采集:Animator/Controller 数量、Clip 驻留、Avatar/SkinnedMesh 占用。
常见信号:
- 大量 Animator 同屏导致内存 + CPU 开销明显
常用抓手:
- 动画裁剪、减少状态机复杂度
- 同屏大量单位:评估 GPUAnimation/烘焙方案(你笔记里已有)
Shader
要采集:Shader/变体数量、运行时编译尖刺、Shader 相关内存。
常见信号:
- Shader.CreateGPUProgram 引发偶发大尖刺
常用抓手:
- 变体裁剪(Strip)、预热(Warmup)、控制首次出现时机
- 材质复用,减少变体/关键字组合爆炸
粒子系统
要采集:粒子实例数、贴图/材质数量、透明覆盖率(Overdraw 关联)。
常见信号:
- 大战斗峰值内存与带宽飙升
常用抓手:
- 贴图压缩/图集、特效分级(低端机粒子上限)
- 控制透明层数与屏幕覆盖率
CPU
动画(CPU)
要采集:Animator.Update、SkinnedMesh、骨骼数量、同屏角色数量。
常见信号:
- CPU Timeline 动画相关模块长期占大头
常用抓手:
- 降低骨骼/动画更新频率、LOD 动画、可见性驱动
- 同屏大量单位:评估烘焙/GPUAnimation
用户脚本(Scripts)
要采集:Main Thread Top Hotspots、GC Alloc、Update 链路、同步等待。
常见信号:
- 持续超预算:脚本逻辑重
- 偶发尖刺:GC/IO/同步加载/锁争用/日志
常用抓手:
- Update → 事件驱动/分帧;缓存引用;减少临时分配
- 资源加载改异步、控制加载时机;尖刺点加埋点证明收益
- UI/字符串拼接/频繁 new 的 GC 治理(你笔记里 NGUI 优化就属于这块)
GPU
渲染(Render)
要采集:GPU Frame time、主要 Pass 成本、相机栈、后处理开销。
常见信号:
- GPU 长期超预算或某些后处理开启后明显变差
常用抓手:
- 开关对比:后处理、阴影、额外光源、Render Scale、MSAA
- 相机栈精简,避免重复渲染路径
填充率(Fillrate)
要采集:全屏后效、透明物、雾/云、UI叠加的像素成本。
常见信号:
- 分辨率一提升就崩、或低端机尤其差
常用抓手:
- 降 Render Scale、降低后效迭代/采样
- 降透明覆盖率、减少全屏叠加层
Overdraw
要采集:Overdraw 视图(Scene/RenderDoc/Frame Debugger 辅助),重点看特效/UI/雾。
常见信号:
- 战斗同屏特效导致 GPU 断崖式下降
常用抓手:
- 粒子分级、控制透明排序与生命周期、降低屏幕覆盖率
Draw Call
要采集:DrawCall/SetPass、SRP Batcher/Instancing 是否生效、材质切换原因。
常见信号:
- SetPass 高、材质/关键字过多导致频繁切换
常用抓手:
- 统一材质与关键字、启用 SRP Batcher、可实例化对象启用 Instancing
- 大世界静态对象:批处理/分块策略(与你仓库B方向一致)
PR 报告输出(可直接复用的模板)
建议每个性能优化 PR 都附上以下内容(即使是小改动,也能极大提升评审效率与可信度)。
1. 背景与问题定义
- 问题描述:
- 影响范围(机型/场景/玩法/频率):
- 复现步骤:
- 预期目标(预算/指标目标):
2. 测试与采样口径
- 设备:高/中/低端机(型号 + 系统版本)
- 构建配置:Development / Release(是否 Script Debugging / 是否 Deep Profile)
- 画质档/分辨率/Render Scale/MSAA:
- 测试 Case:场景名 + 操作路径
- 采样时长与次数:
3. Baseline(优化前)
- 核心指标(建议表格化):
| 指标 | Before | 备注 |
|---|---|---|
| FPS(稳态) | ||
| CPU Main(ms) | ||
| GPU Frame(ms) | ||
| 内存峰值(MB) | ||
| 内存回落(MB) | ||
| GC Alloc(KB/frame) | ||
| 尖刺(ms) | 发生点/频率 |
- 证据截图/文件:Profiler 截图、Memory Profiler Snapshot、Xcode Capture 等(脱敏)
4. Root Cause(结论必须可被证据支撑)
- 归属:CPU / GPU / Memory / IO / 资源驻留 / 变体编译 / 脚本分配
- 关键证据:热点函数/调用链、分配栈、渲染 Pass 组成、材质切换原因等
- 为什么会发生:业务逻辑与引擎机制的解释(避免“只会背结论”)
5. 方案与实现
- 方案描述:
- 关键改动点(模块/类/资源/配置):
- 取舍与风险:兼容性、画面差异、资源包体、线上回滚策略等
6. After(优化后对比)
| 指标 | Before | After | 变化 | 备注 |
|---|---|---|---|---|
| FPS(稳态) | ||||
| CPU Main(ms) | ||||
| GPU Frame(ms) | ||||
| 内存峰值(MB) | ||||
| 内存回落(MB) | ||||
| GC Alloc(KB/frame) | ||||
| 尖刺(ms) |
- 证据截图/文件(同口径):
7. 回归点与发布策略
- 回归覆盖:登录/切场景/战斗/主城/长时间运行/反复进出
- 监控指标:线上 FPS/卡顿率/内存峰值/崩溃率等
- 回滚条件:出现明显画面/性能回退或崩溃上升时的策略