一次重构可能通过所有功能测试,却增加了每帧的处理量。无论代码出自开发者、Claude Code 还是 Codex,这种情况都可能发生:验证行为是否正确的测试,并不测量代码在目标硬件上的运行成本。
性能检查需要可重复的工作负载,以及经过实测的基线。Framedash 的 perf-diff 比较两个构建的遥测数据;run-profile-test 则先启动测试命令,再执行比较。游戏运行在你自己的 PC 或 CI 运行器上。在用结果阻止合并之前,先确认比较测量的是什么,以及同一个构建在没有改动时会有多大波动。
先重复测量未改动的基线
选择一个每次都能按相同步骤执行的场景,例如沿固定路线穿过关卡。先测量基线构建,再不改代码重复运行一次,最后测量候选构建。保持以下条件一致,并随结果一起记录:
- 硬件、操作系统、显卡驱动和电源设置
- 引擎与 SDK 版本、构建配置和图形 API
- 分辨率、画质、VSync 和帧率上限
- 测试场景、相机路线、预热过程、测量时长和遥测设置
重复运行能暴露单次基线测量看不出的波动。如果未改动的构建之间也会触发预定阈值,先排查运行环境或测试流程,再启用 CI 失败判定。直接照抄示例中的百分比,并不等于估算了自己游戏的测量噪声。
还要避免把不同条件下的数据混在一起。perf-diff 按 build_id 汇总查询时间范围内的数据,默认为过去 30 天。重复使用同一个 ID 的运行会计入同一组;ci.scenario 只是元数据,这条命令不会据此自动筛选场景。需要单独比较某次运行或某种配置时,为测量分配不同的构建 ID,并用 ci.commit 保留源代码提交。基线和候选都必须在所选时间范围内有尚未过期的数据。
明确 P50 门禁如何判定
perf-diff 会报告已采集指标的 P50 和 P95。P50 是中位数,性能回归门禁依据 P50 的变化率判定。 P95 用来观察样本值的高位部分,不决定退出码。
安装并配置好 CLI 后,可以用下面的示例检查帧时间:
framedash perf-diff \
--baseline "$BASELINE_BUILD_ID" --candidate "$CANDIDATE_BUILD_ID" \
--api-key-file ci-read.key --metric frame_time \
--threshold 5 --fail-on-regression
两个 ID 必须与 SDK 实际发送的值一致,FRAMEDASH_PROJECT_ID 则设为被测项目的 ID。私有文件 ci-read.key 保存具有 analytics:read 权限的密钥。这里的 5% 是容差示例,不是推荐阈值。
假设基线大于零,帧时间 P50 从 10 ms 升至 10.5 ms,增幅恰好为 5%,这个门禁会通过;升至 10.6 ms,增幅为 6%,则会失败。这些数字仅用于说明判定方式。即使增幅在容差内,报告的 isRegression 也可能为 true,因为 CLI 会另行应用 --threshold。如果只有 P95 变差,门禁仍可能通过,因此关注间歇性卡顿时还需要检查尾部表现。
使用这些选项时,要区分以下情况:
- 不加
--fail-on-regression时,命令只报告结果,不会因为性能回归而失败。加上后,增幅严格大于阈值才返回退出码 1。默认阈值为 0%。 --metric选择用于门禁判定的指标,报告中仍可能包含其他指标。省略时,门禁检查所有可比较的指标,跳过没有数据的指标。因此,帧时间检查通过,并不说明 GPU 时间也接受了检查。- 如果所选范围内没有任何可比较的指标,门禁返回退出码 1。这表示证据不足,而非测出了性能下降。认证错误、无效 ID 等也会导致命令失败;修改代码前,先阅读诊断信息。
其他支持的指标有 memory、gpu_time、io.read_bytes、io.read_time_ms、io.read_ops、load_time_ms 和 mem.vram。每项都需要实际采集到对应数据;I/O 和地图加载测量还需要 SDK 侧的配置。门禁将这些指标的上升都视为恶化。对于 I/O 和加载时间,如果有效基线为 0、候选值为正,则不论百分比阈值是多少都会失败,因为相对于零的百分比变化没有定义。
--map 和 --platform 可以限定数据范围,但不能证明两台机器或两个场景的测量条件可比。--metric load_time_ms 不能与 --map 一起使用,因为地图加载事件没有供该筛选器使用的 map_id。
从 CI 执行测试场景
重复运行的报告能用于判断后,再把游戏启动和性能比较放进同一个 CI 作业。下面是 Bash 示例。./ci/profile-game.sh 代表你自己提供的脚本,并非 Framedash 随附的文件。脚本应启动目标构建、执行场景、完成遥测发送,并以测试结果对应的状态码退出。
framedash run-profile-test \
--command "./ci/profile-game.sh" \
--build-id "$CANDIDATE_BUILD_ID" --commit "$GITHUB_SHA" \
--scenario plaza-loop --api-key-file ci-read.key \
--baseline "$BASELINE_BUILD_ID" --metric frame_time \
--threshold 5 --fail-on-regression
GITHUB_SHA 是 GitHub Actions 提供的提交值;使用其他 CI 时,换成相应变量。基线必须事先完成测量。在校准检查规则期间,可以去掉 --fail-on-regression,先观察结果。
run-profile-test 会在有对应值时,将 FRAMEDASH_BUILD_ID、FRAMEDASH_GIT_BRANCH、FRAMEDASH_GIT_COMMIT 和 FRAMEDASH_TEST_SCENARIO 传给子进程。在 Unity 和 Godot C# 中,先初始化 SDK,再在测试入口读取这些值:
Framedash.TelemetrySDK.Instance.BeginAutomatedSessionFromEnvironment();
// Run the scenario, then flush and wait using your SDK's CI procedure.
Framedash.TelemetrySDK.Instance.EndAutomatedSession();
这段代码只展示会话标记,不是完整的测试框架。UE5 在 UFramedashSubsystem 上提供对应方法。会话会设置顶层字段 build_id,以及 ci.branch、ci.commit、ci.scenario 属性。会话和运行器的配置见 CI 集成性能分析指南。结束会话并不意味着缓冲区中的事件已到达服务器;请按所用 SDK 的步骤刷新缓冲区并等待发送完成,在此之前不要退出游戏。
两个进程使用不同的密钥。CLI 用 ci-read.key 中的 analytics:read 密钥读取数据,游戏则用 events:write 密钥发送数据。通过 --api-key-file 传入读取密钥后,游戏初始化代码仍可从 FRAMEDASH_API_KEY 读取采集密钥。两把密钥都应保存在 CI 密钥管理功能或私有文件中,不要写入版本库或日志。
测量渲染性能时,要启用渲染
UE5 的 -nullrhi 会禁用正常渲染。它可以用于无头逻辑测试,却无法复现渲染时的负载,也不能提供有意义的 GPU 性能比较。要测量图形性能,应在目标 GPU 上启用渲染,并保持两次测试的设置一致。其他引擎的禁用图形模式也应按同样原则处理。
确认测量确实完成
run-profile-test 会等待候选构建的性能事件数高于运行前的值。这能避免重试时仅凭旧数据就结束等待,但不能证明本次运行的所有事件都已到达。游戏进程失败或超时后,运行器也会直接失败,不再执行性能回归判定。
现有 perf-diff 门禁没有统计意义上的最低样本数要求,也不会验证前面列出的所有测量条件。应在测试流程中检查样本数、会话数、场景是否完成,以及 SDK 日志。少量样本也可能生成比较结果,但未必足以支持发布决策。
这些百分位数描述的是已接收的遥测样本,不一定覆盖每一个渲染帧。周期性心跳和事件发生时的快照可能漏掉短暂卡顿;改变事件频率,也可能改变参与统计的样本分布。对于需要限定采集区间、逐帧取样的 Unity 项目,独立的 Unity 性能运行比较试点功能可通过 run-diff 比较基线、未改动的重复运行和候选。它报告帧间隔分布并检查采集是否完整,不执行本文的 P50 性能回归判定,也不会给 perf-diff 增加 P95 失败条件。
调查差异,再用相同条件验证修复
可比较的结果超过阈值时,先在相同条件下重新运行。如果差异仍然存在,再缩小到受影响的负载,用引擎分析器采集详细数据。帧时间或内存增加只能指出症状,不能直接指出负责的函数或分配操作。
如果场景发送了带位置和已注册地图 ID 的事件,性能热力图可以帮助寻找高负载区域。在认定热点来自候选构建之前,先核对构建及其他已启用的筛选条件。构建比较本身不要求配置地图。
智能体也可以通过 Framedash 的只读 MCP 工具协助查看证据。get_heatmap 等聚合工具需要 analytics:read;通过 query 直接执行 SQL,还需要 data:admin。只有调查确实需要直接查询时,才授予更大的权限。能读取遥测,不代表智能体的原因解释或代码修改已经得到验证:仍要用分析器检验假设、评审补丁,并以同一场景测量修复后的构建。
命令选项见 CLI 参考。Claude Code 插件包含 MCP 配置和集成指南:
claude plugin marketplace add crane-valley/framedash-claude-plugin
claude plugin install framedash@framedash
从一个已了解重复运行波动范围的场景开始。把基线、候选报告、测试设置和后续修复验证结果一起保存在拉取请求中,评审者才能据此判断这次性能变化是否可以接受。