slime/research/experiments/normal_search/REPORT.md

5.6 KiB
Raw Blame History

普通搜索:16 个中间位候选成组筛选

2026-10-11;RTX 3060 Laptop GPU;CUDA sm_86。仅研究普通首次输出分支,未修改或测试补扫。

结论:保留公共项预计算和 16 候选成组筛选。相对上一轮最优的“输出差过滤+区间求交”,两个独立的 65,536 随机坐标批次,搜索阶段分别加速 6.51×、6.56×。本轮代码在独立实验入口,未替换正式程序。

同任务实测

预热后轮换模式顺序,七轮中位数。模式 4~7 每轮重新执行 GPU 预计算,耗时已包括预计算 kernel。所有模式使用相同完整低位根列表,完整输出列表逐项比较。

输入 旧最优(模式 3) 32 位预计算(4) 成组筛选(7) 3/7
已知命中位置 0.323584 ms 0.268288 ms 0.156672 ms 2.07×
21 压力/边界位置 1.288192 ms 0.926720 ms 0.223232 ms 5.77×
4096 随机位置 8.857600 ms 5.859328 ms 1.141760 ms 7.76×
65536 随机位置,原批次 130.136063 ms 98.654205 ms 19.982336 ms 6.51×
65536 随机位置,独立新批次 134.184967 ms 102.582275 ms 20.440063 ms 6.56×

另外测试了半差模 5(模式 5),以及半差模 5 配合角落优先检查(模式 6)。均未优于模式 7,不作为最终方案。原批次模式 5/6 分别为 114.548737/108.295166 ms。

这是搜索阶段的 CUDA event 时间,不包括低位根生成、初始化、显存分配、排序及传输。同次运行原批次根生成 kernel 为 267.922424 ms;若每个坐标仍单独生成根,则根生成加搜索的 kernel 时间之和从 398.058487 ms 降到 287.904760 ms,仅 1.38×。这个和是阶段计时相加,不是完整应用耗时。

此前已经验证坐标类之间可以复用低位根,但本实验没有实现全世界分组调度;不可把 6.5× 写成现有完整程序的加速倍数。

公共项预计算

使用归一化种子,令首格坐标项为 0。固定低 18 位 root,中间 17 位记为 m:

a_i = root + term_i
h_i = a_i >> 18
l_i = (a_i XOR C) & (2^18 - 1)
b_i = (A*l_i + 11) >> 17
c = C >> 18 = 0x1790d
q_i(m) = [b_i + 2*A*((m+h_i) XOR c)] mod 2^31

A=0x5DEECE66D,C=0x5E434E432。h_i,b_i 对同一个根的全部 131,072 个 m 共用,每根准备一次。最后公式按 uint32 运算取模即可,无需每个候选重复 64 位乘法。

为什么可以 16 个一起筛

c=0x1790d 的第 4~7 位为 0(最低位编号为 0)。将全部 m 唯一划分为:

base = (g >> 4)*256 + (g & 15),  g=0..8191
m = base + 16*j,                j=0..15

每组仅变化 m 的第 4~7 位:

  • 首格 term 为 0,因此 (m XOR c) 随 j 线性增加,不会跨 256 边界。
  • 对任意其他格,m+h_i 在这段长度 240 的范围内最多跨一次 256 边界。
  • 同一段内,两格的输出都有相同线性增量。因此 (q_i-q_0) mod 2^31 是常数;整组最多两段。
  • 只需计算首尾两处差值,并由进位位置求分界,即可准确判断 16 个候选哪些满足 delta % 10 ∈ {0,8}。

用 16 位 mask 表示一组的候选,逐格与允许 mask 求交,变成 0 就整组退出。最终剩余的具体 m 才进入原有高位区间求交及精确 Java 验证。

每根筛选线程从 131,072 个减少到 8,192 个。仍覆盖所有 m,每个线程所做的工作有所增加,因此实际收益不是理论 16×。这也没有消除中间位维度,只是利用局部等价批量处理。

正确性验证

  • 5,000,000 次随机完整输出对照:预计算公式和原 48 位状态公式一致。
  • 200,000 个随机组,约束数量循环覆盖 1~25:成组 mask 和逐个检查 16 个候选的差过滤完全一致。包括 159,120 个通过的候选,避免只对照空集。
  • 已知位置全部 84 个低位根 × 8192 组,与原逐候选差过滤完全一致,共覆盖 11,010,048 个中间位候选,保留 36 个差过滤候选。
  • 以上共验证 888,128 个完整组 mask。
  • 五组 GPU 基准,全部模式预热与七轮测量均逐项比较完整结果列表;已知位置与压力批次保留原有 24 个命中;随机批次均无命中。
  • 每批最多 64 个坐标的完整低位根列表与 CPU 对照;压力集包括世界边界、int32 边界及原点。

普通分支原有的拒绝重抽覆盖范围未改变。本实验不声称单独覆盖所有 Java 重试解。

全世界量级与下一步

若沿用之前的乐观假设:低位根充分复用、初始化充分摊薄,只用本次搜索阶段吞吐外推 3,750,000² 个锚点,则两批分别约 49.6 天、50.8 天,相比之前约 0.9 年有明显改善。这不是完整全世界运行时间,不含全世界分组、根缓存展开及传输,也不含补扫;不能据此承诺两个月完成。

接下来普通流程优先项:实际接上低位坐标类复用调度,计入分组、候选展开、传输的总成本。随后再研究是否能合并更多中间位组,而不是继续仅压缩高位求解的少量幸存者。

复现

python3 research/experiments/normal_search/run.py

需要可访问 NVIDIA GPU、nvcc、g++。脚本编译程序、运行 CPU 验证、生成独立随机坐标、顺序运行五组 GPU 基准,并写入 results/。不会调用补扫 kernel。

实现:grouped.hpp(预计算和组 mask)、cuda_bench.cu(基准与分组 kernel)、cpu_verify.cpp(独立对照)。原始七轮数据在 results/*.jsonl,汇总与源码哈希在 results/summary.json。

后续已完成计入分组、展开及传输的坐标类复用实验,见 完整批次流程报告。