Notist
On this page
  1. 边界字段分配与调用恢复所有权(2026-11-04)
  2. 1. 先测字段,而非猜测哪个 clone 最贵
  3. 2. 选择最小的所有权改进
  4. 3. 对照与证据边界
  5. 4. 验收结果
  6. 精确归因复核
  7. 普通二进制:六轮配对
  8. 正确性与内存
  9. 后续

边界字段分配与调用恢复所有权(2026-11-04)

接续 签名 COW 与实例复用。本次只处理调用失败恢复的备份,不更改公开 Item/Origin/FunctionId 类型,不改外层 present 的总节点预算恢复,不引入整树 COW、结果缓存或跨轮复用。

1. 先测字段,而非猜测哪个 clone 最贵

在独立源码副本中,分别标记 bind、present 恢复、调用恢复、capture 的 item_shell,并将七个字段的 clone 放进固定容量记录器。观察器将分配转发给相同的 System allocator,不分配记账容器、不对每次操作计时;普通 A/B 二进制不链接该观察器。

完整 IceXIV 的当前基线:

item_shell 字段

分配事件

请求字节,十进制 MB

ctor

413,904

14.079

instance

34,800,031

288.093

target

8,697,058

71.992

origin

41,382,429

2,320.072

evaluation

0

0

fields / attrs

0

0

合计 85,293,422 次、2,694.236MB,其中来源占请求字节的 86.11%。fields/attrs 的共享 Dict 已使它们的 clone 不产生新分配。这不等于其它字典构造、插入或修改也没有成本,也不是整进程分配总量。

按复制用途:

场景

shell 次数

分配事件

请求 MB

bind

4,272,724

13,073,258

349.465

外层 present 恢复

8,389,713

51,156,773

1,593.834

调用失败恢复

4,116,476

21,046,462

750.484

capture_item

5,130

16,929

0.453

这里的事件和请求字节是复制期间的累计流量,不是 live heap、实际复制字节数或 RSS。来源字符串长度取决于本次冻结输入的路径,不把此前另一证据目录的请求字节硬当作本次基线。调用恢复一项中的来源克隆为 674.972MB、实例克隆为 71.992MB、ctor 为 3.520MB。

2. 选择最小的所有权改进

原来的 present_call 已经拥有 Item,却在交给 execute 前另做完整 item_shell,以便 execute 失败时恢复:

拥有原边界 → 复制恢复备份 → 把原边界交给 execute
                             ├─ 成功:丢弃备份
                             └─ 失败:丢弃原边界,使用备份

现在由调用方保留 Option<Item>,execute 借用这个槽位。参数、实现、返回类型等可能产生 Result 错误的操作全部完成后,才 take 边界并组装成功结果:

拥有原边界 → 暂借 execute
              ├─ 成功:take 原边界,移入结果
              └─ 失败:原边界仍由调用方拥有,直接恢复

因此不需要为每次成功调用提前复制身份和来源,也不需要多包一层 Arc/COW。与上一批相同,优先移走不必要的复制,而非让所有复制都变成引用计数。

这不是对求值器状态做事务回滚:已经发生的逻辑读取、步骤、累计额度与诊断仍按原顺序处理,不退款、不吞掉硬预算。原边界 children 在执行前已移出,失败时本就应清空;公开恢复树仍保留原 ctor、instance、字段、注解、来源、span、level,target 与诊断继续由原 error/error_at 逻辑产生。

所有生产改动及新增回归只在 crates/notist-pipeline/src/code/eval.rs。回归覆盖两个 frontend,检查调用前拒绝、参数求值失败、内容返回类型违约、数据返回契约违约四条路径;特意附加字段、注解、来源扩展和 children,验证失败保留正确边界而非部分执行产物。现有循环、资源读取、深度和内容额度测试继续参与验收。

3. 对照与证据边界

证据目录:/tmp/opencode/notist-boundaries-20261104/。before 是上一批已验收 after 的逐文件相同副本;after 仅覆盖 eval.rs。baseline 的 workspace 默认 733/全特性 742 测试日志从该相同快照沿用,明确不声称又执行了一轮;普通二进制仍在本批新目录重建。

tools/boundary_probe.rs 与 tools/profile_boundaries.py 保留字段/场景观察器及冻结工具;普通 A/B、workspace 验证、四组完整语义 JSON、19 次完整构建输出哈希和独立 RSS 采样沿用 tools/profile_runtime_cow.py 的协议。所有脚本在证据目录保存准确副本,源码、输入和二进制另有指纹。

普通主组先完成初次发布,再每版一次 warmup,随后六轮交替顺序的整进程计时;输出核对在进程结束后执行。字段观察器的 wall 不用于计算加速,RSS 组也不混入主组。不会把这轮加速与上一批加速直接相加。

4. 验收结果

精确归因复核

after 字段观察器中,调用恢复场景的 shell、分配及请求字节全部为零;其它三个场景的每个字段记录均与 before 完全一致。总 shell 从 16,784,043 → 12,667,567,shell 内分配事件从 85,293,422 → 64,246,960,请求字节从 2,694,235,862 → 1,943,751,887。

因此可以确认消除的是预期的那一类备份,而非少处理了 bind、挂载或来源。上述仍仅为 shell 范围的分配归因,不宣称是整个 Rust 进程的分配总量。两版字段探针分别用时 19.239s、21.260s,显然受观察器和环境影响,不用于性能结论。

普通二进制:六轮配对

中位数

before

after

wall

14.234s

13.698s

user

12.731s

12.207s

system

1.711s

1.705s

两个 wall 中位数少 0.536s。逐轮配对的耗时降幅为 3.31 / 3.65 / 4.16 / 2.68 / 4.02 / 4.15%,中位数 3.84%,六轮均更快;六样本配对中位数的描述性 bootstrap 95% 区间为 3.00–4.16%。

CPU 频率和系统任务没有完全隔离;保留全部样本,正式主组记录 1 次 major fault。after 的 warmup 曾为 16.330s,按预先规定的 warmup 分组保留、不计入主统计,不事后从正式组删除慢样本。

正确性与内存

  • after workspace 默认 734 项、全特性 743 项通过,74 个 test target,零警告;核心/管线无默认特性 130 项通过。

  • 字段观察器 Rust 单测 1 项、冻结/插桩工具 Python 单测 2 项通过;原通用验收工具单测不改。

  • 21 次全量构建的全部 78,004 个输出文件 SHA-256 一致:普通验收 19 次、字段探针 2 次。四组完整 analysis/evaluation JSON 前后相同;消费者自己的详情/目录/本地 HTML 链接检查通过。

  • 两次独立 Rust PID RSS 峰值:before 6.4891 / 6.4889 GiB,after 6.4912 / 6.4912 GiB,基本不变。消除短命备份没有改变最终树规模或全站保留策略,不宣称降低峰值内存。

源文件、输入、普通及探针二进制、脚本最终指纹复核。仅清理本批外部 Cargo target 的可重建产物;源码快照、二进制、完整输入/输出、测试和观察日志保留。所有生产修改仍未提交。

后续

最大的剩余复制仍是外层 present 的预算恢复。它必须保存改写之前的字段、target、来源与错误顺序,不能简单拿执行后的节点充当原边界。本次不连带修改它;随后再单变量评估廉价 checkpoint,或根据剩余字段成本评估共享来源/身份。全站树生命周期仍由另一专项处理。