# Benchmark：规模、迭代与残差

入口为 [bench/main.mbt](../bench/main.mbt)，使用公开 API 建模和求解。[Node 驱动](../scripts/benchmark.cjs) 校验规模、真实初值、距离残差、构型及报告，并保存环境和每次测量。正式求解库未增加依赖。

## 四档模型

| Case | Points | Vars | Constraints | Scalar equations |
| --- | ---: | ---: | ---: | ---: |
| Small | 10 | 20 | 20 | 22 |
| Medium | 30 | 60 | 60 | 62 |
| Large | 60 | 120 | 120 | 122 |
| Stress | 100 | 200 | 200 | 202 |

四档使用相同的相连三角化折线拓扑：解析位置为 Pᵢ=(i,0)（i 为偶数）、Pᵢ=(i,2)（i 为奇数）。约束包含两个固定点、n−1 条相邻边长、n−2 条跨一点的对角线长度，以及首尾闭合距离，共 2n 条不同关系。固定点各贡献两个方程，因此为 2n+2 个标量方程；实测数值秩为 2n、局部自由度为零，有两个相关方程。没有用重复 Fix 或彼此独立的点凑规模。

单档种子为 `20261022 + 31*n`，Park–Miller / Schrage 生成器预热五步后，为每个目标坐标添加 (-0.05,0.05) 扰动。默认求解选项：长度容差 1e-7、角度 / 方向容差 1e-8、秩阈值 1e-9、最多 200 轮。模型内只用固定点与距离关系，没有圆或角度关系。

## 测量方法

- 本次只测 **JS release**。每档一次不计时预热，随后三次测量，全部在同一个进程中按 Small→Stress 执行。
- 每次调用 `solve()`，不调用 apply；所有预热和重复都从同一份原始初值开始。已验证保存几何逐项不变，不会把已求解模型当成下一轮初值。
- 使用 MoonBit core 的单调时钟，时钟返回微秒。计时只包含 `solve()`：输入校验、归一化、求值 / 导数、迭代 / 稠密线性求解、数值秩和最终报告。排除编译、进程启动、建模、预热、JSON 输出和独立检查。
- 计时区间内发生的运行时分配 / GC 会计入耗时。一次预热与三个样本不足以充分稳定 JIT 或描述延迟分布，不报告吞吐极限或精确性能保证。
- 三次重复是同一模型 / 初值的重复测量，**不是三个不同模型，也不是随机模型的总体成功率**。

独立检查器核对每个固定分量和全部长度残差，要求最大残差比不超过 1.00001，仅给独立浮点运算保留比值裕量；同时与逐约束诊断核对。它检查所有相邻三角形的方向以避免装配翻转，并核对坐标相对解析位置的最大误差不超过 50n 倍长度容差。这一坐标检查阈值与约束残差阈值分别记录。

另有三项定向篡改检查，验证错误几何、错误约束数和 `solve()` 意外改动保存几何会被拒绝。输入错误、未收敛或检查失败均保留原始记录，检查命令以非零状态退出。没有为通过计时设定性能阈值。

## 2026-10-22 实测

环境：Windows x64（10.0.26200），AMD Ryzen 9 8945HX，32 个逻辑 CPU，内存约 31.1 GiB；Node.js 24.19.0，moon 0.1.20261012 / moonc 0.10.15，release 模式。该实现使用稠密求解；下面只反映本机、本拓扑和上述初值范围。

| Case | Vars | Constraints | Iterations，每次 | Status | 最大独立长度残差 | 最大残差比 |
| --- | ---: | ---: | ---: | --- | ---: | ---: |
| Small | 20 | 20 | 4 | Satisfied，3/3 | 7.76827e-10 | 0.00776827 |
| Medium | 60 | 60 | 6 | Satisfied，3/3 | 1.18135e-8 | 0.118135 |
| Large | 120 | 120 | 8 | Satisfied，3/3 | 5.48966e-9 | 0.0548966 |
| Stress | 200 | 200 | 8 | Satisfied，3/3 | 7.16585e-8 | 0.716585 |

| Case | 最小 solve 时间，ms | 中位数，ms | 最大值，ms | 最大坐标误差，原单位 |
| --- | ---: | ---: | ---: | ---: |
| Small | 0.609 | 0.747 | 0.966 | 1.63967e-8 |
| Medium | 2.656 | 2.947 | 4.767 | 1.65395e-6 |
| Large | 16.731 | 17.368 | 17.645 | 2.82311e-6 |
| Stress | 61.742 | 62.483 | 63.012 | 9.89723e-5 |

Stress 的距离残差达标，但坐标误差可以显著大于单条长度容差，体现了较长约束链的误差传播；不能把“最大残差 <1e-7”解释成“每个坐标误差 <1e-7”。这些数据也不证明任意 200 变量模型都可收敛，不是跨机器、跨后端或跨求解器性能排名。

[汇总快照](benchmarks/js.json) 保存机器、工具链、源码 SHA-256 及各档 min / median / max；[12 次逐项记录 CSV](benchmarks/js.csv) 包含初值、结果坐标、规模、迭代、残差及单次耗时。计时保留真实观测，不为美化表格筛选重复。完整原始报告见复现命令生成的 `_build/bench/js.*`。

## 必要时复现

```sh
moon check bench --target js --deny-warn
# 日常轻量检查：只有 Small 一次求解，无预热；不覆盖四档快照。
node scripts/benchmark.cjs --smoke --target js
# 明确需要重测 Benchmark 时：四档各一次预热、三次测量。
node scripts/benchmark.cjs --check --target js
# 审查新数据后更新真实快照与 CSV：
node scripts/benchmark.cjs --write --target js
```

`--check` 核对 CSV 完整性，并报告快照是否对应当前源码；**不会要求新机器的计时等于快照，也不会以计时变化阻塞检查**。其他后端可由 `--target` 显式选择，但本次未测量，不能引用本表作为其他后端的结果。也可用 `moon run bench --target js --release` 只输出原始 JSON Lines。

本次只做 bench 包的 JS 静态 / 格式检查、单次冒烟、必要的四档测量、三个检查器控制项及数据完整性检查。未重跑全量回归、原验证套件、四后端矩阵或包消费测试，提交跳过自动全量 CI；完整验证工作流仅为 Benchmark 增加 Small 冒烟，不在每次提交中重新测量四档；0.2.0 起完整验证仅由维护者手动选择。

类型化错误升级后，以上数据保留为历史测量，本次未重跑。`--check` 独立验证本次新运行的结果，并在源码指纹不同时明确提示历史快照；不会重写已提交测量。加 `--require-current-snapshot` 可要求快照必须对应当前源码，不符即失败。CSV 完整性及当前结果正确性检查仍保留。需要刷新证据时，审查完整实测结果后显式使用 `--write`。
