性能优化的第一步永远是测量,UE 内置的测量入口就是 stat 控制台命令家族。它零成本上手(任何构建、任何平台、连上控制台就能用),但很多人用了几年也只停留在 stat unit 看一眼帧率。这篇把机制讲透:数据从哪来、每个命令看什么、平均数陷阱怎么破。
stat unit:四个数字的解剖
stat unit 的 HUD 输出四行:Frame、Game、Draw、GPU。它们的测量方式不同,关系也不是简单的并列:
- Frame:相邻两帧
FApp::GetDeltaTime()的实际间隔——玩家真正感知的帧时间,包含一切(引擎调度、垂直同步、系统抖动)。 - Game:
UGameEngine::Tick的耗时(GameThread 逻辑侧),从世界 tick 开始到结束,含 GameThread 上等待渲染线程的栅栏时间。 - Draw:渲染线程(
RenderingThread)提交 draw call 的耗时,不含 GPU 执行。 - GPU:GPU 时间戳查询返回的上一帧 GPU 耗时(
FGPUTiming),所以 GPU 数字永远滞后一帧。
判别规则由此而来:Frame 高但 Game/GPU 都低 → 瓶颈在三线程之外(同步、流送、系统);Game ≈ Frame → GameThread 是长板;GPU ≈ Frame → GPU bound。注意 Game 里包含等待 Draw 的时间,所以两线程互相等时数字会"贴"在一起,这时候要看谁先到顶。
stat unitgraph 把四行画成滚动曲线,看尖峰形态比瞬时值有用得多:周期尖峰找 CD/触发源(上一篇的 40 秒案例),随机毛刺怀疑流送和 GC。
stat game 与分组拆解
stat game 展开 GameThread 内部:World Tick 时间、各 Actor/组件 Tick、物理(Physics)、动画(Animation)。每行是一个 STAT_GROUP 的聚合。它的局限是组粒度——只能告诉你"动画花了 4ms",不能告诉你哪个 SkeletalMesh。要细分到自己代码,用 SCOPE_CYCLE_COUNTER 宏包一段代码注册进自定义组,配合 stat mygroup 看。
stat scenerendering 是渲染侧第一刀:InitViews(视锥剔除+光照可见性)、不透明/半透明/阴影各 pass 耗时、DynamicPrimitive(动态路径几何)占比。经验判读:InitViews 高 → 场景对象数或灯光数问题;动态 pass 高 → 静态化/HLOD 没做;阴影高 → 下一刀 stat shadowrendering。
stat gpu 是 RDG pass 级的 GPU 时间分解(依赖 GPU 计时支持),看哪个 pass 吃显存带宽、哪个 pass 异常。它和 stat scenerendering 互补:前者是 GPU 执行视角,后者是 CPU 提交视角。
其它高频:stat rhi(draw call 数、三角形数、显存分配——核对批处理那篇的优化效果)、stat memory(系统内存总账)、stat slate(UI 线程耗时)、stat streaming(纹理流送状态)。
机制与陷阱:平均数会骗你
stat 的每组数据由 FStatsThread 后台线程采集(SCOPE_CYCLE_COUNTER/STAT 宏往无锁队列推计数器事件,stats 线程聚合),HUD 渲染的是滑动窗口平均。这套机制有两个系统性偏差:
平均数掩盖尖峰。帧时间 12ms 平均 + 每 40 秒一次 120ms 卡顿,平均数纹丝不动——玩家却明显觉得卡。所以任何"平均帧率达标"的结论都要用 stat unitgraph 的曲线或 max 值复核;性能验收的指标应该同时有均值和 P95/P99。
Shipping 构建里 stat 大半不可用。stat 宏依赖 STATS 编译开关,Shipping(UE_BUILD_SHIPPING)默认关掉,开发包里能看的只有 stat unit/stat fps 这类不依赖计数器框架的。要给 Shipping 包做 profiling,得用 BuildConfiguration = Shipping 但 bUseStatsInShipping = true 出包(体积涨一点),或者直接用 Insights 的独立采集(Trace 探针 Shipping 保留,这正是 Insights 相对 stat 的结构性优势)。
排查顺序定型
把这套流程固化成肌肉记忆:stat fps 确认现象 → stat unit 定线程 → 按线程选刀(Game → stat game → SCOPE_CYCLE_COUNTER 细分;Draw → stat scenerendering;GPU → stat gpu)→ 平均数可疑就上 stat unitgraph 看曲线形态 → 定位到帧段后换 Insights 下钻。stat 家族解决"在哪一层",Insights 解决"具体哪行"——两把刀,先粗后细。