摘要
接着实验 0500,完整 deep 开局仍约一分钟。这次先用粗粒度计时找出大头:计算状态摘要(局面“指纹”,用来识别重复局面)累计 29.8148 秒,其中 JSON 编码就占 15.4070 秒。于是提出并合入“按兵堆复用已编码的 JSON 字节”:如果一个兵堆对象被证明完全不可变,就直接复用它上次编码出的字节,拼出的整体字节与原编码完全相同。生产版本两组交替测量的中位数从 61.1108 秒降到 46.0613 秒(约减少 24.6%),决策完全相同。但 46 秒仍不满足交互需求,四秒限时内的完整确认覆盖也没有增加。
要回答的问题
完整 deep 搜索的时间主要花在哪?能否在不改动任何结果的前提下砍掉最大的重复成本?
方法
先测量。 45 秒 cProfile 诊断未完成整次决策,插桩开销大,只用于定位;随后在完整 deep 上只给几个入口加粗计时,完整返回 65.9582 秒:
| 入口 | 调用次数 | 累计(含子调用) |
|---|---|---|
状态摘要 state_hash |
46,566 | 29.8148 秒 |
json.dumps |
36,058 | 15.4070 秒 |
状态 to_dict |
24,396 | 6.7004 秒 |
兵堆 to_dict |
351,155 | 5.9137 秒 |
| 完整规则校验 | 11,877 | 9.3075 秒 |
这些区间相互重叠,不能相加。
试过但没有采用的三个方向:
- 给搜索法术评分补传已有子状态:结果不变,但代码核对发现排序路径原本就传了,转移次数仍 10,837 次,没有收益。
- 一次校验内复用状态定义成员集合:三次交替的校验耗时有快有慢,收益不稳定。
- 额外缓存最多 1,024 个状态实例的不可变性证明:只快 1.1–2.2 秒,不足以解决长等待。
采用的方案: 只在兵堆结构被证明不可变、类型与序列化方法仍是规范版本时,按精确对象身份复用它的 JSON 字节;最多 1,024 个兵堆、4 MiB 字节。其余字段(回合、英雄、场地、队列)每次照常编码,按原键序拼接。手工可变输入、未知子类、被替换的编码器都走原路径;公共 to_dict() 仍返回独立可编辑的字典。原型阶段因 Domain 属于共管模块只在独立进程试验;用户回复“全部允许。你自己决定”后才合入生产。
结果
| 检查 | 结果 |
|---|---|
| 原型两组交替(历史) | 66.3961→52.8052、66.7088→52.9914 秒 |
| 生产两组交替 | 60.1181→46.4244、62.1036→45.6982 秒;中位 61.1108→46.0613 秒 |
| 决策一致性 | 完整决策、各根评分/上下界、设置相等;每次 5,836 节点 |
| 27 个冻结局面 | 新旧编码各 2,765 份状态字节逐字节相同 |
| 四份旧保存局 | 120/99/179/54 共 452 个事件及终态重新回放通过 |
| 测试 | 专项 29 项;Domain 全套及消费者 494 项通过(14.74 秒) |
四秒离线控制器在 27 个冻结局面上的对照:
| 四秒内完成的工作 | 旧 | 新 |
|---|---|---|
| 完整筛选 | 68 | 75 |
| 四样本完整确认 | 49 | 52 |
| 至少完成一次确认的局面 | 13/27 | 13/27 |
| 确认后的改选次数 | 2 | 2 |
新增的 3 次确认全集中在原本就已覆盖的一个局面;最终动作全部相同。
局限
- 同一状态的重复测量,不是多局、p95 或网页端到端验收;也不是棋力提升。
- 首次 quick 对照脚本把内存元组和读盘 JSON 列表直接比较而失败,修正比较方式后重跑,没有放宽字节一致性断言。
结论与下一步
等价优化到此收尾,不再叠加小缓存。46 秒的 deep 仍不能直接用于交互;下一步转向战术机制对照(射手输出受限与敌方下一回合威胁),而不是靠缩小 deep 预算宣称“棋力不变”。
Comments