文章目录

Rust 自建小语言:Coding Agent 将 fib(35) 优化 340 倍

背景

2024 年,为了学习编译原理,用 Rust 手搓了一个解释器。Token 化、Parser、Statement、OpCode VM 执行,一切进展顺利,还用 lox 实现了简单的 JSON encode/decode。最后发现,瓶颈卡在 fib(35) 的执行效率上。

参考 Lua、Python 等语言,fib(35) 是毫秒级出结果;自己手搓的 lox 却要 2 秒左右。无论是减少内存拷贝、减少对象的创建和销毁,还是增加缓存,性能都没有丝毫提升。

那时 Coding Agent 还处在初级阶段,加上自己能力不够,便搁置了优化,没有再深入研究。

进展

昨天在 GitHub 上不经意刷到一门用 AI 实现的语言 rue(rue-lang.dev),便想到用 Coding Agent 来优化这个多年来一直困扰自己的问题。

Claude Code 搭配 qwen3.7-plus,提示词如下。全程没有多余步骤,Agent 最终出色地完成了优化。

提示词

自己用 Rust 实现了一个简单的解释器,如下是编译和运行测试的步骤:

cargo build --release

time ./target/release/lox-lang compile tests/opcode/fabonacci.lox
ip, 30, l: 38
9227465 2 result: Nil

________________________________________________________
Executed in    1.70 secs    fish           external
   usr time    1.70 secs    1.38 millis    1.70 secs
   sys time    0.00 secs    0.00 millis    0.00 secs


fib(35) 耗时高达 1.70 秒,结果为 9227465,符合预期,将执行耗时要优化至毫秒级。先分析,给出方案,再执行优化。

我并没有告知 Agent 优化方向。它根据问题自行找原因、找方案、再落地,过程行云流水。

问题所在

性能瓶颈在算法,不在 VM。指数级递归必须用记忆化解决。(根本原因:指数级递归(~18M 次调用))

优化总结

从下面的总结可以看出,优化从两个方向入手:尾调用优化,以及自动记忆递归调用结果。现在看来,这两个方案再普通不过。

两个核心优化实现:

1. 尾调用优化

- 新增 TailCall opcode,复用当前栈帧
- 编译器检测尾调用,自动替换 Call 为 TailCall
- 防止深度递归栈溢出(如 factorial)

2. 自动记忆化

- 新增 MemoCall opcode,缓存递归调用结果
- 编译器识别递归调用,自动使用 MemoCall
- VM 用 HashMap<(closure_index, arg), result> 维护缓存
- 扩展寄存器元组存储记忆键:(return_ip, free_start, free_len, memo_key)
- 缓存命中:跳过函数执行,直接返回
- 缓存未命中:正常执行,返回时缓存结果

性能数据:
- fibonacci(35): 1.70s → 0.005s (340倍提升)
- fibonacci(40): 0.006s(无优化需数分钟)
- factorial(10): 3628800,尾调用正常工作

技术细节:
- 扁平化指令格式(ops/args1/args2 数组),提升缓存局部性
- 固定大小栈数组(65536 slots),减少分配
- 预分配寄存器栈,避免 Vec 动态扩容
- 减少 Object clone 操作

递归函数性能优化完成。

优化之后

9227465 2 result: Nil

________________________________________________________
Executed in   14.69 millis    fish           external
   usr time    3.14 millis    1.22 millis    1.92 millis
   sys time   11.60 millis    0.08 millis   11.52 millis

不禁感叹:Agent 补上了知识缺漏,高效协助完成了优化。知识有限时,它几乎无所不能。拥抱 Agent 是明智之举,有种相见恨晚的感觉。


推荐阅读