资讯动态

从 tsc 到 Go 再到 Rust:编译器重写与 AI 辅助移植的实践思考

发布时间:2026/10/15 5:15:38 来源:尧图企业网站定制
编译器重写这件事平时也就是技术圈里的小范围话题但这段时间的走向确实有点意思。某大型软件公司的 TypeScript 编译工具——一般叫 tsc——刚传出从 JavaScript 迁到 Go 的正式计划社区里已经有人用 AI 辅助完成了一个 Rust 移植版顺手给出了一个相对 Go 版快 1.61 倍的数据。看到这个数字很多人的第一反应是“Go 被吊打了”但我更想聊的是另一层这 1.61 倍到底怎么量出来的AI 辅助移植编译器这类重活靠不靠谱以及如果自己动手做类似的事真正的坑在哪里。这篇内容适合对编译器实现、语言选型感兴趣的人也适合在犹豫要不要把 AI 用在大规模代码迁移项目上的团队。1. 一场由“重写”引发的连锁反应从 JavaScript 编译器切换到 Go 再冒出 Rust1.1 原版编译器“慢”在哪问题根源不是语言不好而是架构太旧先说原版 tsc 的情况。它最早是用 TypeScript 本身实现的所以它的运行环境是 JavaScript 引擎比如 Node.js。整个编译过程包含词法分析、语法分析、AST抽象语法树构建、类型检查、代码生成这几个阶段全部在 JavaScript 的运行时里完成。JavaScript 确实很灵活动态对象很适合描述 AST 节点。可问题也出在这一个大型项目可能有几千个模块、几十万个 AST 节点而 JavaScript 对象是带动态属性的每一个节点的读写都涉及运行时属性查找、原型链操作、垃圾回收。让一个编译器跑在这种环境里等于是让一位会计师一边用卡片目录查账一边还要顺手维护这套目录。编译一个小项目还行编译一个大型工程时CPU 和时间就全花在对象访问和内存管理上了。很多人以为 tsc 慢是因为“代码写得不好”其实架构层面就注定了它的瓶颈。尤其是类型检查阶段TypeScript 的类型系统是图尔完备级别的还要做跨文件的符号解析和类型推断这一步不是简单的线性拼接它涉及大量引用、缓存、查询。每一次从 AST 节点到类型信息的跳转在 JS 里都扛着不小的开销。1.2 Go 被选为替代语言不是单纯追求快而是稳妥和可控某大型软件企业宣布采用 Go 重写这套编译器时我第一反应不是“Go 是不是最好”而是“这个选择够务实”。Go 的优势在于上手成本低工具链简单交叉编译方便内存占用相对可预测。对于编译器和工具链这类项目开发效率和可维护性往往比极限性能重要得多毕竟以后还要养这个项目好多年。从工程角度说Go 做了两件很对的事一是把运行时从 JS 换成了静态编译出的二进制没有 JIT 预热的过程冷启动性能大幅改善。二是引入了协程并发模型这样解析多个文件时可以更自然地并行原版 JS 的单线程模式很难利用多核。但 Go 的 GC垃圾回收仍然是一个需要面对的成本。AST 这种东西生命周期短、创建频繁、引用关系复杂GC 在扫描和回收这些临时对象时会不断停顿这在大量编译输入的场景下表现其实不差只是还没有做到“极致”罢了。1.3 社区接力的真正价值用 AI 把“翻译”这个体力活自动化重写方案公开没多久出现了第三股力量有人用大模型辅助把 Go 版核心逻辑翻译成 Rust。这件事听起来是“编译器的编译器”实际的操作模型非常朴素把 Go 代码拆成一个个相对独立的模块丢给模型做逐函数的语义翻译人工只负责检查和修正。这类移植有一个显著特点它不是设计重写而是逻辑迁移所以不需要人从零想架构最大的工作量是在代码翻译和编译通过的过程里。AI 恰好擅长这种“语义保持”型转换因为每一段 Go 代码的目标都是对应一段等价 Rust 代码不太容易产生创造性分叉。于是原本可能要几个人干几个月的活被压缩成了一个人加几个晚上。于是问题来了Rust 版真的必然比 Go 版快吗要回答这个得先搞清楚那个 1.61 倍是怎么来的。2. “比 Go 版快 1.61 倍”是怎么被量出来的2.1 先搞清楚基准测试范围否则 1.61 倍没有意义任何“快 X 倍”的结论都得看它是在什么场景下测出来的。是只测了解析阶段还是包括了类型检查是单个文件的小项目还是接近真实规模的巨型代码仓库是冷启动还是连续多次运行很多移植类项目最容易踩的坑是只挑一个对自己有利的测试样本。一个几千行的库文件和一个几百万行的业务系统对编译器的压力模型完全不一样。前者可能是 IO 和启动时间主导后者才是真正的 CPU 计算密集型场景。如果某个基准只在小型模块上表现突出那么“快 1.61 倍”就只能代表那一类输入而不是整体共识。从社区给出的信息来看这个数据更接近“在若干公开大项目上的编译耗时中位数对比”也包含了类型检查过程。这类结果具备一定参考价值但你要真拿去给自己的项目做选型依据我建议还是把测试用例换成自己的代码再跑一遍。2.2 Go 版本受限于运行时与 GCAST 生命周期是杀手那么理论上Rust 为什么有机会更快先看 Go 编译版本的几个潜在瓶颈。第一个是 GC 停顿。编译过程会产生海量短命对象比如每一个 token、每一个 AST 节点。Go 的 GC 设计目标是低延迟对大堆的处理还算平滑但大量对象分配与指针关联会让 Mark-Sweep 的开销持续累积。编译到一半GC 突然跑一轮几毫秒到几十毫秒的停顿看起来不大但在几万个文件上累积起来感知就不一样了。第二个是接口的动态派发。Go 的interface{}很灵活可一旦把 AST 节点存成接口类型再反复断言回具体类型类型查询和内存访问就会变得不直接。编译器自己的数据结构高度依赖接口与字段查找Go 在这类抽象上的效率不如把类型写死时那么高。第三个是调度模型。Go 的协程适合并行处理但每个协程都要有一个栈和调度开销。如果你把“一个文件一次协程”设计得过于激进反而会因为调度和栈扩容吃资源。2.3 Rust 版本快在哪儿所有权、零成本抽象与更高效的内存布局Rust 的优势不是因为它“天生比较快”而是它提供了很多能把热点压到更低的工具所有权与生命周期可以让你设计出免 GC 的 AST 结构。节点之间不需要反复引用计数而是按拥有者关系一次性释放内存。枚举enum表达 AST 节点时内存布局紧凑匹配时编译器能直接生成跳转逻辑没有动态属性查找。Arena 分配器可以把同一编译阶段的节点连续分配在一起内存局部性更好CPU 缓存命中率远高于 GC 堆上随机分布的对象。借用检查器虽然在写代码阶段很烦但一旦能编译通过你就知道对象引用关系是明确的并行读取不会发生数据竞争这也让多线程解析更安全。所以 1.61 倍并不是“Rust 魔法”而是一些很具体的工程差异叠加出来的结果。窄化到数学过程Rust 在编译管线里消灭了大量隐式分配和间接跳转得利的自然就是最后的数字。3. 如果自己做一次“AI 辅助移植”最关键的工程步骤是什么3.1 先拆结构整个编译流水线有哪些模块编译器不是铁板一块它是由几个松耦合阶段组成的。要移植先得把它拆开。以 tsc 这类 TypeScript 编译器为例核心模块大致有这些模块功能移植难度Scanner扫描器把源码拆成 token 流低多是对状态机的翻译Parser解析器根据语法规则构建 AST中需要定义树节点结构Binder绑定器建立符号与作用域关系高涉及复杂的数据关联Checker类型检查器类型推断、类型比较、报错极高代码量最大逻辑最绕Emitter生成器生成 JS 输出、SourceMap 等中相对独立移植方便如果你想用 AI 做翻译从头到尾一次过是不现实的。正确做法是按模块分阶段拆逐个突破。Scanner 和 Parser 的结构最容易描写清楚也可以作为验证 AI 输出能力的试金石Binder 和 Checker 才是真正决定成败的部分因为类型系统的数据结构和递归推断逻辑非常绕。3.2 配置 AI 任务与提示词一次只翻译一个编译阶段实际操作层面我发现把大模型当成“翻译器”而不是“架构师”效果最好。不要直接扔给它整个仓库说“帮我把这个项目转成 Rust”而是把单个文件、单个函数、甚至单个数据结构作为单元然后给它限定边界。一个我实测下来还算稳的提示词结构是这样的任务把下面这段 Go 代码翻译成 Rust。要求保持函数语义完全一致不要额外增删功能对没有直接对应的库函数使用 Rust 标准库中功能最接近的实现输出的 Rust 代码必须可以独立编译不依赖尚未定义的代码。这样做的意义是隔离上下文。AI 如果一次性接收到几十个文件的互相引用很容易产生幻觉代码或者重复定义。一次一个小单元人工审查每个输出是否符合预期看起来慢但长期看反而快因为后续阶段的错误不会层层叠加。最好还用“Git 分支”方式管理进度每完成一个模块的翻译并且测试通过就提交一次。如果 AI 在某一步把逻辑改跑了可以很快回退到上一个可用状态。我见过有人让 AI 连续翻译了十多个文件才想起来要测试结果一个类型推导错误藏在中间定位和修 bug 的时间比重新翻译还长。3.3 语义等价性验证移植过程中最容易偷工减料的地方比翻译本身更重要的是验证“翻译后的代码与原代码行为一致”。编译型语言之间即使语义等价也会有边界差异。尤其是类型系统TypeScript 的类型是结构化的、运行后会被擦除的而 Rust 的类型是紧凑与静态的这会让 Checker 的移植非常痛苦。所以必须准备一套差异测试选取一批不同规模的 TypeScript 文件让原版 tsc 和 Rust 版分别编译然后对照输出内容是否一致。不仅要有“编译成功”的结果还要对比编译错误信息、错误位置、生成 JavaScript 的内容。只要有任何一个用例输出不一致就说明移植的某个环节出了问题必须回到对应模块排查。这类语义等价测试建议从最开始的 Scanner 就建立起来不要等高难度模块写完了再测。因为越到后期你越难判断错误是来自 Parser、Binder 还是 Checker。尽早把基线锁住后续模块的移植本质上就变成了“保持绿测试”的约束问题。4. 编译器移植的风险与避坑实录4.1 性能数据的真实陷阱基准热身、缓存与长尾“比 Go 版快 1.61 倍”这类数据第一次出现时很多群里都开始讨论要换语言。但如果你自己去做基准测试会发现几个特别容易误导人的细节。第一是否有足够的热身时间。编译器的 JIT 可能不存在但操作系统缓存和处理器频率调度会显著影响结果。如果只跑一次冷启动硬盘 IO 会掩盖 CPU 阶段差异如果连续跑多次文件系统缓存又会偏向第一次读取数据的那个版本。第二项目样本是否公平。必须保证两个版本处理的是同一批源码、同一批 TypeScript 配置且生成的输出完全等价。有些人为了突出某个版本的性能会故意减少类型检查深度或跳过某些诊断模块这属于“刹着车和别人比加速”。第三长尾用例有没有纳入统计。平均时间是 1.61 倍不代表每一个文件的编译时间都差了 1.61 倍。很可能大部分文件的提升只有 1.1 倍但几个大型依赖图复杂的文件贡献了特别大的差距。这种差异对用户体验的影响很直观但如果只看数字会误以为全链路都获得了同样收益。4.2 类型检查器不同于解析器移植难度差异极大如果只看外围很多人会以为编译器移植就是“把函数换个写法”但真正动到类型检查器时才会感受到难度。TypeScript 的 Checker 存在大量内部数据结构比如符号表、类型映射、缓存池、错误报告队列。这些结构在 JS 里的实现方式和 Rust 的所有权模型天然不太兼容。举例说TypeScript 使用“结构化类型”做判断两个类只要结构相同即使声明来源不同也可能被认为是兼容的。Rust 则默认是名义类型你需要自己实现一套类似对象的兼容性探测逻辑这不是简单的一对一翻译而是要在 Rust 的类型系统之上构建一套影子类型系统。这就是为什么很多社区移植版会选择“在 Rust 里内嵌一套自定义 TypeScript 类型表示”等于把一个虚拟类型引擎跑在高性能基座上。能做成功说明作者对类型系统的理解相当扎实如果只是机械转译大概率会在这一层折戟。4.3 我评估这类数据是否可信的几条判断标准分享几条我个人的评估习惯供你参考看源码是否公开、构建步骤是否可复现。一个只给结论不给构建方法的 1.61 倍参考价值有限。看是否做了输出等价性检查。如果它只测了 AST 生成耗时但不比较最终产生的 JS那测试是不完整的。看测试环境有没有明确列出来。CPU 型号、内存容量、操作系统版本、Rust 编译优化等级每一项都会影响结果。看对比对象的编译配置是否一致。比如 Go 版是否开了并行Rust 版是否用了发布模式的优化配置这些不能在基准测试里一个开一个关。除了这些技术动作我也很看重作者对“瓶颈究竟在哪里”的解释。只丢出一个时间数据说明可能只是把基准跑完了能讲清楚瓶颈是内存分配、类型缓存还是 GC 停顿才说明他真的把一个编译器项目吃透了。提示无论最终结论如何都不要因为一个数字就仓促决定“全盘切换语言”。编译器和工具链是长期投资性能只是其中一个维度开发效率、团队熟悉度、第三方生态支持往往比那 0.6 倍更重要。4.4 给尝试“AI 辅助大型移植”的人几条具体建议如果你现在跃跃欲试想用同样方式移植一个不算小的项目我建议你先做下面三件事第一先挑一个最小的完整功能路径试水。不要一开始选核心模块先选一个从输入到输出都容易验证的外围模块。比如一个格式化代码的工具或者一个独立的代码生成器。把这条路走通后AI 产出的质量上限和你的修正成本就可以估算了。第二判断你是否愿意维护生成代码。AI 生成的代码可以很干净也可能很怪。如果项目需要长期维护这些代码要交给团队里的其他人那么“可读性”比“运行速度”更重要。别为了 1.61 倍性能落入代码没人敢改的泥潭。第三保留自动测试和监控。不管是移植前还是移植后持续集成里必须有性能回归测试。编译器这类基础工具一旦性能回弹影响面往往是全公司所有项目的构建时间。老老实实把基线数据和最新数据都摆出来才是最有力的说话方式。5. 这种“语言竞速”给普通项目的启示整个事件看下来最值得普通开发者借鉴的不是“Rust 比 Go 快”而是一个更通用的项目管理经验任何性能优化或架构重写都该以清晰可比较的基准为前提。 1.61 倍这个数字只有在别人能复现的环境和步骤下才有长期价值否则它就像一条没有坐标的天气预报知道了个趋势但很难指导你明天穿什么。从我个人的经验来说编译器移植大概是所有软件项目里最不适合“凭感觉做”的领域因为它的输入输出可量化、可对比、可自动化验证。只要你把等价性测试和基准测试建立起来AI 的高效翻译就能被你牢牢关在笼子里而如果没有这两个护栏AI 反而会成为生产 bug 的加速器。与其争论 Go 和 Rust 谁更好不如先琢磨琢磨自己手头最依赖的工具是否有一份可靠的性能基线。对于想把项目迁移到新语言或者想复用 AI 做大规模代码翻译的团队这才是最值得带走的东西。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑