摘要

接着实验 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 预算宣称“棋力不变”。