diff --git a/design/qa/test-perf-report-2026-10-02.md b/design/qa/test-perf-report-2026-10-02.md new file mode 100644 index 00000000..19ab197c --- /dev/null +++ b/design/qa/test-perf-report-2026-10-02.md @@ -0,0 +1,139 @@ +# 现行自动化测试·性能验证报告(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%。