Files
gplan-2/design/qa/test-perf-report-2026-10-02.md
T

14 KiB
Raw Blame History

现行自动化测试·性能验证报告(2026-10-02)

任务书:.zcode/plans/test-perf-audit-prompt-2026-10-02.md(用户委托批——只测量与报告,零改码零改断言)。 执行:独立会话,全程主工作区(master)、全程串行(任一时刻至多一个本会话 Godot 测试进程)。

0. 环境与测量前提

  • 引擎 Godot_v4.7.2-stable_win64_console.exe;cwd=D:\code\g-plan2;素材不入 git 照旧。
  • 测量期间 master 被并行会话连续落地(见 §6.3):批次归属两树态——
    • T1 态 = 5e501a6(18:04 前):#1 冒烟×4、#2 run_all×4、#3/#4/#5 探针批次;
    • T2 态 = 725f0ac(现行头):#5–#11 全部探针、#14–#16、#12/#13、新码复验均在 725f0ac。
    • 绿件在两态下读数一致(冒烟 60.55 vs 62.20s;run_all 12.16 vs 12.75s)——落地零计时漂移。
  • 并行会话的 Godot 进程(含一个 1GB 窗口化实例)与本项目测量同窗活跃,秒级件上的负载噪声见 §3 波动性。
  • 工作区在飞件(game/core/battle_orchestrator.gd 注释空白、game/project.godot stretch/aspect 行、未跟踪 game/tests/probe14_test.gd)只标旗未触碰。

1. 计时总表

协议:每件先跑 1 次记冷启动,随后正式 3 次取中位;外部 python 计时包装(%TEMP%\perf-audit-20261002\perf_run.py),零插桩。#12/#13 按任务书走小样本协议(单跑推算)。

# 测试 冷启动 正式 3 跑 中位 极差 极差% 判读锚(exit)
1 全量冒烟 run_smoke 61.77 60.55 / 61.96 / 60.54 60.55s 1.42 2.3% 0(636 项 0 失败×4)
2 run_all 15 套件 12.36 12.00 / 12.45 / 12.16 12.16s 0.44 3.7% 0(all green ×4)
3 ui_probe 1.82 1.82 / 2.05 / 2.69 2.05s 0.86 42.0% ⚠ 0
4 ui_input_probe 2.40 2.44 / 2.55 / 2.52 2.52s 0.11 4.4% 0
5 ui_click_probe — 双态全红,见 §6.1 — — — 旧态 exit=1 ×3;现行头 240s 看门狗击杀 ×4
6 fx_probe 2.58 2.47 / 2.49 / 2.47 2.47s 0.02 0.8% 0
7 ui_layout_probe 1.75 1.73 / 1.74 / 1.75 1.74s 0.02 1.4% 0
8 ui_tune_probe 9.50 9.26 / 9.33 / 9.41 9.33s 0.15 1.6% 0
9 shot_probe(窗口化) 5.23 3.67 / 3.51 / 3.68 3.67s 0.17 4.5% 0(冷启动+42%=首跑 shader 编译)
9a └ shot_checker 断言 1.26 0.86 / 0.89 / 0.88 0.88s 0.02 2.7% 0(机检面全过)
10 fx_shot_probe(窗口化) 6.93 6.89 / 6.86 / 6.94 6.89s 0.08 1.1% 0
11 board_shot_probe(窗口化) 3.36 3.28 / 3.30 / 3.44 3.30s 0.16 4.8% 0
12 MC 蒙特卡洛(小样本) — N=50×2 策略=100 局 486.0s 4.86s/局 — — 0(100 RUNSUM)
13 balance_verifier(小样本) — --quick 36 局 173.7s 4.83s/局 — — 0
14a gdd_lint 0.53 0.31 / 0.31 / 0.31 0.31s 0.00 1.3% 1 ×4(8/8 文件不通过,见 §6.2)
14b doc_link_audit 0.98 0.41 / 0.41 / 0.41 0.41s 0.00 1.0% 0
14c doc_term_audit 0.41 0.38 / 0.38 / 0.39 0.38s 0.01 1.6% 0
15 repo_structure_audit(熵增闸) 1.10 1.07 / 1.04 / 1.06 1.06s 0.03 2.4% 0
16 static_scan.sh 0.84 0.75 / 0.64 / 0.76 0.75s 0.12 16.4% ⚠ 0(S1–S8 可执行子集全过)

与任务书「已知量级」对照:冒烟 ~65s→实测一致;run_all「数十秒级」→实测 12s 更快;窗口化三件「十秒级」→实测 3.3–6.9s 更快;MC「预计最长」→证实(推算满档 ≈65 分钟,见下)。

#12/#13 满档推算

  • MC(-- 50 80000 both min,100 局实测):normal 档 ≈1.21s/局(前 50 局 ~60s),perfect 档 ≈8.47s/局(后 50 局 ~423s,≈7 倍——perfect 选路/战斗更充分)。现行 80000 代链各档 400 局满档推算:normal ≈8.1 分钟 + perfect ≈56.5 分钟 ≈ 65 分钟/全档;单策略 400 局 = 8 / 56 分钟。
  • verifier(--quick 36 局 = (8 冒险+10 战役)×2 画像,173.7s):4.83s/局。满档 na=60/k=60 = (60+60)×2 = 240 局 ≈ 19.3 分钟。

2. 冒烟分段耗时与 180s 闸余量

方法注记:管道模式下 GDScript print 为块缓冲(全量退出时才刷,逐行时戳全聚在 60.445s)——段级改用 --verbose 强制行刷法 2 跑(61.0 / 63.6s,较常态中位膨胀 ≤5%,占比可信);winpty 伪终端在本环境断言失败不可用(备选已试、弃)。verbose 双跑 636/0 全绿、零 SCRIPT ERROR。

180s 硬闸(ADR-0004,run_smoke.py 外部超时)余量:180 − 60.55 = 119.45s(余 66.9%,冒烟仅用闸的 33.1%)。分段合计 61.7s ≈ 墙钟,无未计入空洞。verbose 双跑段级极差全部 <10%(最大 5.7%),段占比稳定。

最慢 Top10 段(两跑中位;各自验证内容)

# 段(smoke_all.gd 行号) 中位 占比 验证什么
1 冒险结构验收(:277) 21.30s 34.5% 自动玩家三关完整连探:三层奖励框架行值单点/大奖励兑付/摘要五件事/账本封闭——全冒烟最重的端到端段
2 关卡节点地图·分支节点图 L2(:2818) 18.58s 30.1% 图合法性校验(正反例)+ 幽暗地域图版行进
3 SaveManager 存档读写/迁移/兜底(:3684) 9.69s 15.7% 存档序列化全链:写盘/读回/版本迁移/兜底/隔离档——纯逻辑段里唯一的重 IO 段
4 关卡节点地图·线性推进(:737) 3.56s 5.8% L1 全链自动推进(战×2→道具→战×2→恢复→Boss)
5 遭遇随机化(:1764) 2.81s 4.6% unit_count 区间骰/encounter 流/可回放——大量掷点重放循环
6 商店房三收口(:3201) 1.00s 1.6% 商店经济链(现价/加算区/余额不足拒/离开回选路/收束清零)
7 护甲等级掷点(:851) 0.77s 1.2% 查档纯函数+暴击+伤害下限+AC12 分布(批掷点)+棋盘必中
8 行动经济·轮内刷新(:1396) 0.63s 1.0% 方案 A:Active 相位当场换图继续挖
9 敌人技能池(:2439) 0.46s 0.7% 开轮摇号/冷却保底两闸/三乘法链/独立子流
10 棋盘生成(:1035) 0.32s 0.5% 棋盘生成公式面

集中度:Top3 段 = 49.6s,占冒烟 80.4%;Top6 = 93.3%;其余 37 段合计 ~4.2s。 冒烟耗时本质=三关完整冒险×2 条路径(结构验收+L2 图版)+存档 IO,其余全是亚秒级。

全部 43 段中位明细(秒)

21.30 冒险结构验收|18.58 分支节点图 L2|9.69 SaveManager|3.56 线性推进|2.81 遭遇随机化|1.00 商店房三收口|0.77 护甲等级掷点|0.63 轮内刷新|0.46 敌人技能池|0.32 棋盘生成|0.24 清图奖励双份兑付|0.22 棋盘布局格边长公式|0.20 轮内刷新 provenance 守卫|0.17 GP escrow 隔离|0.16 GP 冒险钱包到账|0.16 格面资源资格面|0.16 插旗不可逆四情况|0.14 冒险结构 API 闸|0.12 完整战斗回放 L2 熊地精|0.11 极性路由|0.11 攻击类型×护甲克制|0.11 完整战斗回放 L1 地精|0.10 负向字典逐型直验|0.08 地形变异布置段 L2|0.08 主角乘区接线|0.08 地形变异布置段 L1|0.07 fresh 池攻击链|0.06 元进度收束入账与工坊|0.05 五段全序+行型封闭|0.05 旗动作耗尽末点 AP|0.03 PlaytestLogger|0.02 元进度注入可见与白板|0.02 战斗日志视图行格式化|0.01 元进度几何定价与购买|<0.01×9:伤害量值区间/DifficultyTable/格面资源九型/地形变异框架面/格面资源身份面/冒险表/MetaTable 结构/换装护栏/商店房图不变式

3. 波动性标注(极差 >10% 的项)

项 极差% 绝对幅度 判读
ui_probe 42.0% 0.86s(1.82→2.69) 2 秒级件上的绝对小值;测量窗内并行会话 Godot 进程活跃(其 1GB 窗口化实例同机)所致负载噪声,非测试自身不稳
static_scan.sh 16.4% 0.12s(0.64→0.76) 同上,亚秒级件 + 内部多路 grep 子进程对负载敏感
shot_probe 冷启动 +42% 5.23 vs 中位 3.67 窗口化首跑 GPU shader 编译,单次成本 ~1.6s,后续稳定

其余全部件极差 <5%(多数 <3%),段级极差全部 <10%——环境噪声可控,全部中位读数可作分级依据。MC/verifier 为小样本单跑协议,无极差数据(协议如此)。

4. 分级建议(数据支撑,对照 06 在册「呈现层测试分级与 UI 驱动夹具周期化」条口径)

06 条现行口径(2026-09-26 用户裁):每次变更必测=run_smoke 全量 + ui_input_probe(触碰 presentation/ 加 ui_click_probe + ui_layout_probe);周期测=B 档视口截图/C 档黄金图/真人目检/MC 大扫描。实测数据对该口径的支撑与加码:

A. 每次变更必跑池(headless 全套 ≈87s,单机可承受)

件 中位 依据
run_smoke 全量 60.6s 闸余量 119s;耗时本质=Top3 段(三关冒险端到端+存档 IO),裁剪空间小、回归价值最高
run_all 15 套件 12.2s 便宜且覆盖公式/状态机/契约/致命类协议
ui_input_probe 2.5s 06 条点名必跑,实测仅 2.5s,维持
ui_probe / ui_layout_probe / fx_probe 1.7–2.5s 秒级件,触碰 presentation/ 时顺手加跑无负担(06 口径内扩展,建议追认)

B. 周期池(维持 06 口径,数据加码)

件 中位/推算 依据
ui_tune_probe 9.3s 探针里最贵(调整板/批量模拟通路),建议触碰调参/模拟通路时跑,否则归周期池尾
窗口化三件+shot_checker 3.3–6.9s+0.9s 单件不贵但需真渲染+桌面弹窗在场,06 裁 B 档=周期(样张/表现批收口窗)——数据支持维持(另见冷启动 shader 编译 +1.6s)
MC 大扫描 满档 ≈65 分钟(perfect 档 8.5s/局 7 倍于 normal) 06 已归周期,数据强支持;如需快速回归可用 50 局/策略小样本 ≈8 分钟
verifier 满档 ≈19 分钟 周期跑;--quick 2.9 分钟可作调参会话随跑档
ui_click_probe — 修复并重测后再定级(现状见 §6.1:载体解析错误损坏;修复前旧态常态打满 20000 帧 budget=141s,与其注释自述「常态通关远低于此」相悖——即使修复,141s 级也建议从「触碰 presentation 必跑」降到周期/发布前,待绿态实测重裁)

C. 提交钩子级(全件 <1.2s,零负担):doc_link_audit 0.41s / doc_term_audit 0.38s / repo_structure_audit 1.06s / static_scan 0.75s——适合挂 pre-commit/pre-push;gdd_lint(0.31s)挂钩前须先清 §6.2 的 8/8 红账,否则常红无用。

5. 交付与复现

  • 本报告零改码达成(计时=外部包装,分段=--verbose 行刷,MC/verifier=文件头用法小样本)。
  • 原始日志与逐跑记录:%TEMP%\perf-audit-20261002\(results.jsonl + 逐件逐跑 .log,43 段解析在 segments_verbose.json)——系统临时目录,不属仓库。
  • verifier 小样本在 design/qa 落过 balance-verifier-20261002-192230.{md,jsonl} 两件生成物,计时提取后按中间产物规则已删除(可复现)。
  • 复现命令全集(cwd=仓库根):任务书 §三 表格所列即全部;补充两条——分段计时 godot --headless --verbose --path game --script res://smoke/smoke_all.gd;MC 小样本 godot --headless --path game --script res://smoke/adventure_monte_carlo.gd -- 50 80000 both min。

6. 异常与偏差记录(照实记录,未当场改码)

6.1 ui_click_probe 双态全红(本批最重要发现)

  • T1 态(5e501a6):3 跑 141.0–141.5s(极差 0.3%)exit=1。现象=循环退出(done=false budget=0)问题=0——20000 帧 budget 打满未走完全流程,内部零断言失败;伴随 TypedArray push_back 错误刷屏 ~1 万行。并行会话提交 cd1ed95 的提交信息自证:该 budget 耗尽+typed-array 错误经纯 master 对照=1:1 同源(master 既有,归 T17 在飞批)。
  • T2 态(b0c5d7e…725f0ac,现行头):载体直接损坏——ui_click_probe.gd:343 解析错误 Expected statement, found "Indent"(343–344 行为悬挂代码片段:一行注释后残留 print 尾部实参),脚本加载失败→探针场景无逻辑→永不 quit、零输出,4 跑全部 240s 看门狗击杀(wall 241.7s 无计时意义)。值得注意:cd1ed95 自述修复过一处解冲突解析错误,但 343 处仍残留一处。
  • 复现:godot --headless --path game res://smoke/ui_click_probe.tscn(现行头必然零输出挂死——探针无自退出护栏,外部超时是唯一防线,正好坐实 ADR-0004 防挂死条款的必要性)。
  • 关联:同窗 ui_probe 判别实验 2.06s 正常(排除环境系统性故障);smoke/run_all 不经此载体故不受扰。

6.2 gdd_lint 4 跑全 exit=1:8/8 GDD 文件不通过

规则命中含表格占比(00 号 37%/02 号 84%)、R3 正文修订注记三条、R4 缺「版本历史」节等(07 号亦红)。量级 0.31s 不是问题,红账本身是问题——修复责任不在本测量批,建议归并行文档批/T17 在飞批清账后重跑。复现:python tools/gdd_lint.py。

6.3 测量中途 master 四度落地(并行会话)

18:04 b0c5d7e(演播后排窗修复+t17_defer_probe)→ 18:23 ee00810 → 18:31/18:32 时间戳两笔 → 18:51 c2e4be3/725f0ac(真时间戳)。本批以「两树态归属」方式消化(§0),绿件跨态读数一致(冒烟 60.55→62.20s、run_all 12.16→12.75s,均 <4%)——T17 演播后排窗批对两闸计时与正确性零漂移。

6.4 环境注记

  • 并行会话 Godot 进程同窗活跃(含 18:11 起一个 1GB 窗口化实例长期闲置):本会话侧保持串行铁律不破;秒级件的负载噪声已在 §3 标注。
  • 管道 stdout 块缓冲 + winpty 不可用 → 段级耗时走 --verbose 法(§2,膨胀 ≤5% 已量化)。
  • 冷启动普遍便宜(除窗口化 shader 编译),「预热 1 次」协议对绝大多数件影响 <5%。