第 9 章 · 07 代码生成 vs 解释执行
本节摘要:拿到 AST 后,有两种实现策略:编译(把 AST 翻译成机器码,跑得快但平台相关)与解释(直接遍历 AST 执行,慢但跨平台)。还有折中:字节码 + 虚拟机(先编译成字节码,再由 VM 解释/ JIT)。本节讲清三种策略的取舍,让你为自己的语言选对实现路线。
内容来源:基于编程语言实现整理的导读。
学习目标
- 说清「编译成机器码」与「解释执行」两种策略。
- 解释各自的取舍:性能 vs 跨平台 vs 实现复杂度。
- 理解「字节码 + 虚拟机」折中方案(Java/Python 的选择)。
- 简述 JIT(即时编译)的概念。
一、学习价值:为你的语言选对实现
你造一门语言(第 9 章编译器线),拿到 AST 后,选哪种执行策略决定它的性格:
- 追求极致性能(C/Go/Rust):编译成机器码。
- 追求快速开发/跨平台(Python 早期/Ruby/Lua):解释执行。
- 折中(Java/Python 当前/C#):字节码 + VM(可能配 JIT)。
理解这三者,你为自己设计的语言能做出合适选择,也理解为什么不同语言性能差异巨大。
二、认知拆解:三种策略
1. 编译成机器码(AOT,Ahead-Of-Time)
AST → 生成目标机器码(或汇编→汇编器→机器码)。生成的可执行文件直接跑在 CPU 上。
- 优点:性能最好(无解释开销)。
- 缺点:每平台一份(x86/ARM 各编译一次);实现复杂(要懂目标架构指令集、寄存器分配)。
- 代表:C/C++/Go/Rust。
2. 解释执行(树遍历解释器)
AST → 直接遍历执行每个节点。if 节点 就判断条件、+节点 就把两子节点求值相加。
- 优点:实现简单(不用懂机器码);跨平台(只要宿主语言跨)。
- 缺点:慢(每次执行都遍历 AST,重复工作)。
- 代表:Python 早期、Ruby、Lua 早期、本书建议你第一个语言用这策略。
3. 字节码 + 虚拟机(折中)
AST → 编译成「字节码」(一组紧凑的虚拟指令,如 LOAD/PUSH/ADD)→ 一个「虚拟机」程序解释执行字节码。
- 优点:字节码比 AST 紧凑、遍历快(线性指令流),跨平台(字节码平台无关,VM 移植即可)。
- 缺点:仍比机器码慢(VM 解释有开销)。
- 代表:Java(JVM 字节码)、Python(.pyc 字节码)、Lua。
进阶:JIT(Just-In-Time 即时编译)
在运行时,把「热点」字节码动态编译成机器码执行。兼顾跨平台(分发字节码)与性能(热点跑机器码)。Java HotSpot、JavaScript V8、PyPy、C# CLR 都用 JIT。实现复杂,第一个语言不必做。
为你的语言选哪种
- 第一个语言:树遍历解释器(最简单,「Crafting Interpreters」前半本教这个)。
- 想追求性能:升级到字节码 VM(同书后半本)。
- 进阶:JIT。
三、上手第一步
造第一个语言:实现树遍历解释器。eval(ast) 递归求值每个节点。变量存环境(作用域字典),控制流(if/while)直接对应遍历。参考「Crafting Interpreters」。
本节要点回顾
- 编译(AOT):AST→机器码,快但平台相关、实现复杂(C/Go)。
- 解释(树遍历):直接遍历 AST 执行,慢但简单跨平台(Python 早期)。
- 字节码+VM:AST→字节码→VM 解释,折中(Java/Python 当前)。
- JIT:运行时把热点字节码编译成机器码,兼顾跨平台与性能(V8/HotSpot),实现复杂。
- 第一个语言选树遍历解释器,参考「Crafting Interpreters」。
推荐上手顺序
- 实现树遍历解释器(eval AST)。
- 支持变量、if、while、函数。
- (进阶)设计字节码,写 VM 解释字节码。
- (高阶)实现简单 JIT。
下一节是全书收尾——经典学习路径与书目。
作者与出处
原作者:灏天文库
来源:codecrafters-io
许可证:CC0-1.0
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习