CD Code Digest Hub /GTA V

性能体系:关键路径、工作集、预算与可验证的优化

这是跨模块综合分析。源码事实引用对应专题和本章证据表;公式是分析模型,实验与指标是新项目建议,均非实测结果。未编译、运行或基准测试工程。入口:Summary。

22 分钟阅读 15 个章节 2 张图

1. RAGE 性能设计的主轴#

RAGE 的性能特点可以概括为:离线把内容转为紧凑布局,运行时维持有限活动集合,用任务重叠可并行计算,再在明确阶段交付结果。它不是单靠某个快速 allocator、SIMD 内核或大线程池支撑开放世界。

最值得学习的是系统性的成本转移:

手段移走的成本新增的约束
资源内存镜像、布局匹配每次装载解析与细分分配版本、对齐、指针 fixup、工具正确性
定义/实例分离重复内容存储与构建实例和共享资源各自生命周期
SoA、量化、紧凑索引cache miss 和带宽编码上限、误差、数据更新一致性
异步 IO/计算/渲染主线程串行阻塞排队、发布、延迟回收和峰值内存
LOD、sleep、虚拟 voice无意义的持续全量工作质量损失、唤醒成本和恢复状态
固定池、scratch、有界队列热路径分配与不可预测增长峰值失败与降级策略

优化往往把问题从一个阶段推到另一个阶段。报告中任何“更快”都应理解为待验证的机制收益,而不是对当前硬件的百分比承诺。

2. 必须分清四种预算#

2.1 容量预算#

例如任务句柄池、dependency ring、composer nodes、物理 manifold pool、loader slots、drawlist pages。它们限制同一时刻可存在的工作或数据,不是每秒吞吐或整帧时间保证。

2.2 服务预算#

例如 streaming conversion 时间片、每帧处理批次、audio voice cost、网络目标字节。它们限制一个阶段愿意投入多少资源,不必然能限制一个不可分割工作的最长耗时。

2.3 截止时间#

pose 必须在物理或绘制消费前就绪,碰撞资源必须在高速移动物体到达前可用,音频块必须在设备消耗前供应。截止时间取决于消费者,不能只用统一的“后台低优先级”表达。

2.4 观测预算#

PF_*_BUDGETED 的 budget 是 timebar 标记参数;脚本 debug warning、profiler 阈值也不等于执行抢占。把告警线当成硬预算,会误判系统已经具备过载保护。

3. 帧时间由关键路径决定#

对一张已知依赖图,理想计算完成时间至少为:

T ≥ max(W / P_effective, L)

其中 W 是总 CPU 工作量,P_effective 是实际可用并行度,L 是最长依赖链。调度、锁、cache、外部设备和系统抢占只会进一步增加时间。这是下界模型,不是引擎自带计时公式。

图是依赖分析示例,实际阶段和交错由游戏注册/调用点决定。它说明:缩短一段不在关键路径上的后台任务可能没有 FPS 收益,但可能减少资源迟到;减少主线程收尾或延迟依赖可能比再加 worker 更有效。

流水线充分重叠时,长期帧吞吐常接近最慢阶段,而单次输入到呈现延迟包含多个阶段及缓冲等待。提高吞吐与降低输入延迟不能互相代替。render-done semaphore 只证明相应 CPU 渲染阶段完成,不能当作 GPU fence。

4. 任务粒度、拓扑与线程超额订阅#

4.1 更多任务不必更快#

若 N 个任务、每次调度成本 h,总计算 C,则简化开销为 N*h;每个任务太小会放大原子、句柄、event 和 cache 转移成本。批次过大则降低可分摊性,最后一个重任务决定完成时刻。

粒子按 emitter item 分批,而 emitter 的粒子量不同;物理按写冲突分 phase,而各 phase 工作量不同;动画按依赖图推进,而深链无法靠宽线程池拆开。三者需要不同分组策略,不能用一个全局 batch size 解决。

4.2 专用线程与 worker 争用同一组核心#

RAGE 同时有 dependency/task、render、audio、streaming、pathserver 等执行者。PC worker 数和 affinity 带有旧平台假设;应统计总 runnable 数、核利用率、迁核和实际排队,而不是只看某一个池的“空闲线程”。

混合大小核、多 CCD/NUMA 环境下,固定轮转亲和性可能破坏 cache 局部性或把 deadline 工作放在较慢核心。是否绑核需要测量;现代 CPU 核心多不是原配置天然更快的保证。

4.3 优先级不等于可推进性#

Dependency 四级共享 ring 高优先先取,回调失败可重排,未发现该段公平老化保证。提升动画优先级可能降低 pose 等待,却推迟为下一帧提供资源的低优先级工作。队列满自旋又会消耗消费者本应获得的 CPU。

建议追踪 ready width、queue age、执行时间、等待原因和 producer/consumer ID;同时让负载超过预算时有帮助执行、限流、拒绝或降级策略。此为新项目方向,不是声称现有调度器已具备全部机制。

5. 大世界是一条有状态服务流水线#

5.1 资源可用延迟的分解#

L_ready = 排队 + 依赖等待 + 读盘 + 解压/转换 + placement + 发布等待

各项可部分重叠;对于依赖链,应按实际依赖关键路径计算,不能简单把所有节点耗时全相加。硬盘换成 SSD 只直接改变其中一部分;若 placement 或主线程发布已成为瓶颈,IO 更快只会让完成队列堆积更快。

源码明确区分 loader slot、资源状态和异步完成标记,最终主线程 SetResource 后才发布 Loaded。应按这些状态采集时间,而非只统计 ReadFile 耗时。

5.2 预取距离必须覆盖端到端延迟#

简化直线运动模型:

D_prefetch ≥ v_peak * L_ready,p99 + D_margin

v_peak 是需要支持的移动速度,L_ready,p99 是端到端可用延迟的高分位,D_margin 包括预测误差、相机提前可见和资源使用范围。若资源还需物理或导航注册,相应时间也要计入。

转向、飞行、镜头切换和瞬移违反直线假设,需要额外兴趣点、显式预热或可见降级。世界显示距离增加会同时扩大地形/实体、纹理、导航和碰撞工作集,不能只调整 LOD distance 后期望一切线性变化。

5.3 稳态吞吐与突发缓冲#

设到达率 λ、服务率 μ,长期 λ 大于 μ 时任何有限队列都会耗尽。稳定条件下 Little 定律 平均在途数 = λ × 平均停留时间 可帮助解释为什么增加在途 slot 会增加内存,却未必减少延迟。

稳定场景下的 λ/μ 不足以覆盖瞬移和过场切换。需要记录短窗口突发字节、依赖扇出、placement 个数和回收滞后。loader 的 pending read 阈值和 conversion budget 是局部背压,不能自动组成整帧全局预算。

5.4 滞回和依赖闭包#

导航 Store 有装载/卸载距离差;世界 LOD 需要 loaded/attached 状态。它们都抑制边界来回抖动。模型卸载还需考虑贴图、动画、物理、脚本和绘制引用构成的依赖闭包。

过早回收导致重载,过晚回收增加驻留;单独追求缓存命中率会掩盖长期 pin。应同时观察 eviction 后短期重载比例、资源最后使用时间、引用来源以及实际可释放字节。

6. 内存峰值比稳态图表更危险#

分析上可按互斥类别记账:

M_peak = 常驻 + 活跃实例 + 在途输入 + 未发布输出 + 延迟回收 + 碎片/对齐 + 临时工作区

CPU/GPU 双份、defrag 新旧拷贝应归入对应项一次,避免重复计数。这是记账模型,不是某个 allocator 的原样字段。

峰值来源容易误判的现象应采集的信息
取消中的 IO/placement请求取消了,内存仍未下降cancel 与 completion 两个时间点
render 引用和 GPU 使用对象移除,纹理仍不可释放CPU 引用、提交帧、设备完成边界
defrag 搬移总空闲尚多却分配失败最大可用块、pin、搬移临时双份
动画过渡角色数不变但 scratch 暴涨活跃图和中间 frame 峰值
碎裂/唤醒平均物理对象很少仍爆池单帧新增 collider/manifold/contact
多兴趣点玩家附近统计正常但驻留过大每兴趣点边际资源与共享依赖

固定池没有消除内存问题,只把问题转换成容量和故障语义。开发版本的堆回退会掩盖发行版本上限;离线校验应根据发行约束执行。

Defrag 也不是免费恢复按钮:源码有 copy/place/finalise、多帧状态和 render flush,包含带宽、引用修复和等待。它适合被测量并控制频率,不应作为持续超预算内容的补救。

7. 数据布局的收益和代价#

场景 descriptor SoA、量化 BVH、physics bitsets、动画 block、cloth 八边批处理,都在减少有效工作所需读取的字节。优化顺序通常应先缩小活动集合,再让访问连续,最后才是单条算术指令。

对照简化 Roofline 思路:内核时间至少受 max(运算量/可用算力, 数据量/有效带宽) 限制,但随机依赖访问还可能受 latency 限制;不能只以理论内存带宽推导实际速度。

量化降低字节和 cache 占用,同时引入误差与 decode;SoA 提高批量读取效率,同时让单对象修改涉及多数组;预取只有在访问可预测且距离合适时有益。cache.h 的 prefetch 平台分支甚至可能为空实现,某条旧主机注释中的收益不能跨平台照搬。

共享原子计数器可能成为 coherence 热点,邻接字段也可能 false sharing。仅有 16-byte 对齐不保证 cache-line 隔离;布局应结合平台 cache line 以及实际读写所有权检查。

8. 物理优化:接触图比刚体数量更有解释力#

Broadphase 减少候选对,BVH 减少几何候选,manifold 维持接触,solver 消化约束。一个“有多少刚体”的数字不足以描述成本:稠密堆叠、关节链、自碰撞、composite children 和碎裂会改变 pair 数、窄相复杂度及并行冲突。

SplitPairs 按共享物体冲突安排并发 phase,scratch 限制 phase 数;同一堆物体的约束高度相连时,可并行宽度可能很小。增加核心并不改善约束传播收敛,减少 iteration 又可能增加穿透或关节误差。

CoreUpdate 将 velocity solve、position integration、collision、push solve 等按特定顺序组织;更改顺序会改变接触信息的新旧与稳定性。g_MaxPushCollisionIterations 是外层 push/collision 相关次数,不是通用 solver iteration。以这个常量推算所有约束成本会出错。

Sleep 同时降低模拟与接触活跃集合,但集中唤醒是另一类峰值。物理 substep 由游戏时间片策略决定,不是所有配置固定 60 Hz;性能实验要记录真实 dt、slice 数、迭代数和 active set,避免将质量变化误当优化收益。

建议测量:候选对→有效 manifold 比例、每 collider 接触数、phase 工作量分布、solver wait、约束残差、穿透、wake 数及异步 shape-test 量。查询预算应与仿真预算分开,减少 solver 不能消除大量镜头/AI/脚底射线成本。

9. 动画优化:控制语义与姿态质量必须分开#

动画成本包括状态更新、采样解码、混合、IK/表达式、骨架传播、物理交换、任务构图和等待。角色总数、总骨骼数和图编辑器总节点数都只是间接指标。

Motion tree composer 的有限 dependencies/frames/proxies 表明中间结果存活区间是第一类资源。过渡叠加、附属角色依赖和深层 blend 可同时放大计算与 scratch;应先削减不必要节点和重复映射,再考虑把每个节点拆得更细。

不可见角色仍可能需要时间、root motion、动画事件和物理骨骼。当前 AnimDirector 的特定入口没有简单遵照旧 mover-only 注释,不能将“按可见性跳过 pose”当作已普遍实现的事实。但新项目确实可以把状态 tick 与 pose 求值分级,前提是保证 late compose 不再推进时间,镜头突变时恢复 pose,并保留所有消费者需要的骨骼依赖闭包。

压缩率、采样缓存命中与 CPU 时间应联合考虑。人群同 clip 不同 phase 时可能不共享 block;随机 seek/切动作会打破顺序缓存。PoseMatcher 的线性扫描也不适合无测量地扩成巨大动作库。

推荐同时记录视觉质量:足底/手部误差、根轨迹、脚滑、事件重复/漏发和 ragdoll 过渡速度连续性。FPS 变高但玩法事件遗漏,不是有效优化。

10. 渲染优化:把提交与 GPU 成本拆开#

绘制列表、实例化、排序键和可见性筛选降低 CPU 提交成本;GBuffer、deferred lighting、阴影、透明和后处理消耗不同 GPU 资源。CPU drawlist pages 与 GPU draw call 数没有简单一一对应。

减少 draw calls 可能增加单批包围体、实例数据或过度绘制;更大 LOD 合并资源可能减少提交却放大 streaming 粒度;减少几何不一定降低大面积透明烟雾的像素成本。优化必须同时看 CPU build/submit、GPU pass 时间、可见像素和资源带宽。

HiZ/occlusion 使用历史或阶段结果时,要处理镜头跳变与对象运动带来的保守性。不能通过图形路径名字推断现代 GPU-driven/clustered 特性;本源码应按实际实现评价。

11. 跨系统过载会形成反馈环#

图描述可能的系统反馈,不是声称已测得现有游戏必然出现此环。其启发是:只给单个模块扩池或加线程,可能让系统更晚但更剧烈地触顶。

Audio voice 虚拟化、网络分批/限流、Fragment 禁止破坏标记都展示了局部降级思路。新项目需要统一优先级语义:玩家控制、关键碰撞、音频连续性、可选粒子和远景更新的损失容忍度不同。降级必须保持必要玩法结果,同时避免频繁升降质量形成抖动。

12. 观测体系:从计时条走向因果链#

源码已有 PF timer/value/timebar、trace、GPU marker、stall 检查和 Bank,但启用受配置约束;Timebar Core 默认回调为空,DeviceProfiler 的一个 IO 路径当前关闭。测量前应证明标记实际生效、时钟可比较、事件没有溢出。

建议每条异步工作携带关联 ID,记录:

  1. request/create:谁需要它,内容/对象 ID 与截止阶段。
  2. queued/ready:何时入队,依赖何时满足。
  3. start/end:在哪个线程/核心执行,实际服务时间。
  4. publish/consume:结果何时合法发布,谁等待了它。
  5. cancel/retire:取消意图与最后安全释放分别何时发生。

聚合统计应至少分 p50/p95/p99、最大值、超预算比例与连续超预算长度;平均 FPS 不能显示偶发半秒停顿。若线程区间重叠,不能把所有 CPU timer 相加当帧时;inclusive/exclusive 也不能混用。GPU 时间不能直接由 CPU 调用持续时间替代。

13. 针对新项目的实验矩阵#

以下仅为可执行的未来研究设计,本次没有运行。

实验控制变量重点观测通过条件应如何定义
Job 合批扫描固定总计算,改变任务/批量提交、queue age、最后任务、scratch帧尾收敛且吞吐不以更多等待换取
跨系统峰值动画/物理/placement 同时突发runnable、锁、关键路径deadline 任务仍按时,可选任务有界退让
世界高速遍历固定路线,改变速度/冷缓存ready p99、missing、IO/placement在目标速度下关键资源不迟到
瞬移与取消连续请求不同区域并中断在途字节、取消延迟、残留引用内存最终回落,无陈旧发布
长期装卸相同区域反复进出largest block、pin、defrag、reload峰值不持续爬升,无回收风暴
物理拓扑相同刚体数,分散/堆叠/关节链pair、phase、误差、耗时每类质量门槛均满足再比较速度
物理突发破碎与全局唤醒pools、查询、solver wait容量失败可诊断,必要行为不丢失
动画多样性同角色数,clip/phase/层数变化cache、活跃图、pose 峰值质量与事件正确,资源上限明确
镜头突变快速切镜与室内外切换scan、LOD、late pose、upload无长期缺块/错误姿态,峰值可界定
GPU 覆盖率固定粒子数改变屏幕面积pass 时间、overdraw、带宽明确 CPU 与 GPU 限制切换点
音频与 IO世界冷装载时大量声音ring 水位、starved、解码成本设备供给不因图形峰值中断
网络压力控制丢包/抖动/码率queue age、state age、误差最新状态收敛且内存有界

每轮比较应固定构建配置、内容版本、机器电源/频率策略、相机路径和采样窗口;记录 warm/cold 条件。先重复 baseline 确认波动范围,再比较更改,不以单帧最好值证明收益。

14. 优化排序与迁移决策#

建议先验证生命周期和容量,再按关键路径选择项目:

  • 第一优先级:消除无意义工作、重复资源请求、同步强制装载、陈旧查询和错误引用。收益通常跨系统,且能改善稳定性。
  • 第二优先级:工作集和数据布局,包含 LOD、sleep、图裁剪、量化、批量和中间结果复用。必须有质量门槛。
  • 第三优先级:执行重叠、任务拓扑、发布阶段和服务优先级。需要全局时间线证明关键路径缩短。
  • 最后精调:预取、intrinsic、亲和性和特定硬件路径。只对已定位热点做,并保留可比较的基线。

应迁移的是显式所有权、数据/实例分离、工具验证、阶段完成语义和高水位观测。应重新设计的是老硬件亲和性、SPU scratch 数值、光盘寻址启发式、共享可变 singleton 和依赖隐含于主循环的部分。

15. 源码证据与专题关联#

证据本章使用的事实
timebars.h28–35 细节配置与 no-op 回调;80–86 budget 传参
profiler.h__STATS、group/page、FlipElapsedTimes
stream_profiler.h14 IO profiler 关闭条件
cache.h平台 cache line、对齐与 prefetch 分支
streamingloader.cpp297 起主线程收尾;1603 起内存失败处理
多线程报告调度结构、scratch、优先级与等待
内存报告、资源报告分配器、known references、离线布局
Streaming 报告、世界报告依赖闭包、发布、defrag、LOD 和空间工作集
物理报告phase 冲突划分、时间片、sleep 与 shape tests
动画报告中间 frame、阶段完成、压缩与缓存
渲染报告、特效报告CPU/GPU 分离、SoA、透明与破坏峰值
音频报告、网络报告连续 deadline、voice 预算、同步 age 和限流

这份源码最重要的启发是把性能问题写成可观察的因果链:谁产生工作,数据在哪驻留,等待哪项依赖,何时交付,以及过载时放弃什么质量。只复制某个局部技巧,无法复制整套系统的效果。