Notist
On this page
  1. notist-eval 场景基准:第一轮证据
  2. 范围与复现
  3. 1. 最强信号:不执行的表达式仍产生遍历/分配成本
  4. 2. 相同完整结果,native 构造明显贵于已物化树
  5. 3. 重复绑定内容不等于复用最终结果
  6. 4. 宽参数与严格局部绑定也有独立成本
  7. 5. 小 JSON 的解析缓存收益有限,剩余成本仍大
  8. 其它结果与边界
  9. 下一轮建议(未实施)
  10. 验证记录

notist-eval 场景基准:第一轮证据

范围与复现

本轮只增加基准、检查和只读 checked-program 访问器,没有优化求值器或改变语言行为。驱动位于 tools/eval-bench;完整说明见该目录 README.md。

PYTHONDONTWRITEBYTECODE=1 python3 tools/run_eval_bench.py --out /tmp/opencode/eval-bench-NEW

本次原始证据目录:/tmp/opencode/notist-eval-bench-20261107-final。包含源码快照、workspace.patch、构建日志、独立二进制、依赖 feature、原始样本、summary.json 和 report.md。manifest.json 的完整性检查成功,结果文件哈希另行复核一致。该目录在 checkout 外,未进入版本控制。

  • 19 类场景、58 个实例。一般规模为 1/16/128,嵌套深度为 1/4/8/16。

  • 使用真实 .notc / Markup 编译得到 checked IR,直接调用生产 notist_eval::run。

  • 每个场景在 3 个新进程中各测 7 批,共 1,218 个计时样本;批次约 33–45 ms,顺序打乱。

  • 下文时间为「各进程中位数的中位数」,包含求值和结果销毁,不包含编译、校验、HTML、磁盘 IO。

  • 分配诊断来自另一个启用 alloc-metrics 的构建,各场景测 3 次,不使用其时间。

  • 输入、模块和 JSON parsed cache 为明确预热的状态;不代表冷启动或完整站点构建。

  • AMD Ryzen 9 9950X3D2,rustc 1.97.1,release,CARGO_INCREMENTAL=0;未固定单核、频率或隔离后台负载。

  • 各场景的三个进程中位数最大/最小差距为 0.3%–5.9%;不是置信区间。

1. 最强信号:不执行的表达式仍产生遍历/分配成本

capture_dead_branch 的 name + name + … 位于未选中的 else 分支。条件是函数参数 flag,运行时传 true;表达式使用平衡树,避免同时线性增加栈深。pruned 对照改为 if true,让检查器提前删掉 else;它是控制变量,不是通用语义替代。

场景

规模

checked 表达式数

输出节点

µs/eval

alloc 次数

运行时分支

1

8

4

2.255

101

运行时分支

16

38

4

3.809

176

运行时分支

128

262

4

15.331

736

提前裁剪

1/16/128

4

4

约 2.029

90

规模 128 时,运行时分支相对裁剪对照约 7.56 倍耗时,分配请求流量为 54,888 vs 8,795 bytes。源码 crates/notist-eval/src/eval.rs 的 capture_expr 对 If 的条件和两个分支都递归代换,而实际 expr 求值只执行选中分支。这与观测相符:分支没有执行,并不代表捕获/复制它免费。本轮尚未做 helper 级 CPU tracing 或替换实现的 A/B,不能把全部差额归因于一个函数。

负对照 unused_content:未挂载 opaque call 的函数体从 1 增至 128 个元素,耗时仍约 1.82 µs、82 次分配。因此不是「所有未使用代码都被执行」;需要区分内容配方的透明捕获和 opaque 调用边界。

2. 相同完整结果,native 构造明显贵于已物化树

ready_text 与 native_text 已通过完整结果相等测试,不只比较可见文字。

规模 128

输出节点

µs/eval

alloc 次数

请求流量 bytes

ready_text

130

13.254

11

50,214

native_text

130

79.477

3,088

206,064

差距约 6.00 倍。ready_text 仍要生成本次结果并销毁,不是零工作基线。native 构造额外包含 checked invocation、参数处理、分派和元数据物化。这支持继续检查通用调用路径和构造开销,但不是宣称能让真实文档整体快 6 倍。

code_calls/128 为 207.329 µs、7,982 次分配、259 个输出节点;capture_uses/128 为 91.770 µs、3,508 次分配、131 个输出节点。小 Code 调用值得关注,但两者边界/身份/节点数不同,不能当等价实现直接计算优化收益。

3. 重复绑定内容不等于复用最终结果

规模 128

µs/eval

输出节点

alloc 次数

replay_value

107.710

259

3,893

replay_call

189.115

387

7,096

同一个绑定重复挂载仍会产生每次挂载的树与身份;opaque call 配方还会执行调用。两个场景不是语义/边界完全相等的替换,差额不能全部称为「重复求值浪费」。后续共享不可变配方可以考虑,但最终挂载身份和可观察字段不能直接共用或丢弃。

4. 宽参数与严格局部绑定也有独立成本

many_parameters 从 1 到 16 到 128:1.749 → 6.085 → 49.915 µs,分配次数 79 → 177 → 903。输出都是 4 个节点,但调用字段数量增长。execute 对每个实参在线性参数列表中 find,源码存在宽调用 O(P²) 的查找结构;当前数据也包含字段构造、scope、输入克隆等,不能据此把全部时间归因于查找,或声称整体已呈平方增长。

strict_lets 从 1 到 128:1.588 → 10.817 µs,输出始终 4 个节点。这是严格绑定本来需要完成的工作,不应通过跳过绑定来获得语义不正确的加速。

5. 小 JSON 的解析缓存收益有限,剩余成本仍大

输入为 12-byte 的 {"name":"x"},规模 128 时逻辑读取均为 128 次,输出均为 259 节点。

场景

µs/eval

alloc 次数

峰值增量 requested bytes

json_uncached

453.563

17,775

336,221

json_cached

394.144

16,073

192,909

缓存使耗时下降约 13.1%,但不会绕开每次逻辑读取、授权/依赖计数、Data 操作和调用包装。这说明本例不能只靠 JSON parse cache 解决执行成本;不代表大 JSON、复杂投影或其它数据形状收益也小。uncached/128 的分配次数在三次诊断中为 17,773–17,776,表中为中位数;没有过滤异常样本。

其它结果与边界

  • loop/128:148.234 µs、132 节点,包含 int→string 与挂载,不是纯循环调度开销。

  • recover_errors/128:130.761 µs,核验 128 个预期诊断,避免把错误提前退出当性能提升。

  • pending_reads/128:176.888 µs,返回资源请求而非部分结果;0 个输出节点不代表没有工作。

  • on_demand_instances/128:206.399 µs,code_calls/128 为 207.329 µs;小差额在进程波动范围内。此处 Context checked-plan cache 已预热,不能推出冷检查/编译免费。

  • nested_expansion 深度 1→16:2.240→16.062 µs,节点 5→20;深度、边界和 provenance 一起增加,不能全部归为来源链成本。

  • 所有场景分配诊断的结果销毁后 net-live 增量中位数为 0;这不是内存泄漏证明,也不是进程 RSS。

  • 请求流量是 alloc bytes + realloc 目标大小之和,不等于实际复制量;峰值是相对已准备输入/程序/cache 的 requested-live 增量。

下一轮建议(未实施)

优先单独验证「捕获代换 → 共享配方 + 环境」的 A/B,保留当前场景作为语义/性能回归基线。其次拆解 native 构造与宽参数调用,分别检验参数槽位、静态分派和元数据物化。所有实验都应保留来源、调用边界、错误恢复、挂载身份与完整输出对比,不把省略语义当优化。本轮只提供排序依据,不预测 icexiv 全站 CPU 或峰值内存改善;外围全量驻留与构建调度需要独立测量。

验证记录

  • workspace --all-features 串行测试:775 passed、0 failed(补充 pruned 对照前)。

  • 最新 bench package --all-features 串行测试:8 passed、0 failed。

  • Python benchmark 汇总测试:3 passed。

  • 正式运行中全部 58 场景经准备核验、每批后完整 canonical outcome 对比,全部通过。

  • timing 构建未启用 alloc-metrics 或 notist-eval/test-support。