掘金 人工智能 14小时前
传统编译器系列 - 第1节 编译器的基础概念
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了编译器和解释器的核心区别,以及JIT和AOT两种编译策略。文章详细介绍了Pass和IR中间表示等关键概念,并结合华为Ascend & MindSpore平台的编译器课程内容,为读者提供了GCC、LLVM、AI编译器等入门知识。此外,文章还通过实例分析了编译器在大厂中的应用,并提供了面试题和场景题,帮助读者更好地理解和掌握编译器技术。

💻 编译器与解释器是两种将高级语言转换为可执行代码的主要方式。编译器(Compiler)负责一次性翻译并生成机器码,适用于追求性能和稳定性的场景;解释器(Interpreter)负责运行时逐条解析与执行代码,更注重灵活性和交互性。

🚀 JIT(Just in Time)即时编译和AOT(Ahead of Time)提前编译是两种不同的编译策略。AOT在程序运行前一次性编译为机器码,启动速度快,运行效率高;JIT在运行时动态地将热点代码编译为机器码,可根据运行时信息进行优化,但启动慢,首次运行需编译。

⚙️ Pass是编译器在处理源码时的一个阶段性转换逻辑,负责词法分析、语法分析、语义检查、中间代码生成、优化处理和目标代码生成等功能。IR(Intermediate Representation)是介于源代码与机器码之间的一种中间层代码形式,解耦了“源语言”与“目标架构”的强绑定,使得一个编译器可支持多语言,一个语言可支持多平台。


传统编译器系列 - 第1节 编译器的基础概念

本节内容基于华为 Ascend & MindSpore 平台的编译器课程内容,结合传统编译器理论与现代实践,系统讲解编译器与解释器的区别、JIT/AOT 编译方式、Pass 和 IR 中间表示等核心概念,是学习 GCC、LLVM、AI 编译器的入门前置章节。


一、编译器与解释器的区别

编译器(Compiler)与解释器(Interpreter)是将高级语言转化为可执行代码的两种主要形式。两者的本质差异如下:

对比维度编译器(Compiler)解释器(Interpreter)
执行方式编译后整体执行边解释边执行
输出生成目标代码不生成中间目标代码
存储保留中间码(可执行文件)不保存中间码
错误处理一次性显示全部错误遇到错误才停止并提示
执行效率高(提前编译)低(解释执行)
示例语言C/C++、Rust、Go、Java(AOT)Python、Ruby、PHP、JavaScript

简言之,编译器负责一次性翻译并生成机器码,而解释器负责运行时逐条解析与执行代码。


二、JIT 与 AOT:两种编译策略

AOT(Ahead of Time)提前编译

程序在运行前一次性全部编译为机器码,例如:

优点:

缺点:

JIT(Just in Time)即时编译

程序在运行时动态地将热点代码编译为机器码,例如:

优点:

缺点:

对比图示:

编译模式启动速度最终性能应用场景
AOT稳定工业部署、嵌入式
JIT灵活AI 推理、浏览器、虚拟机

三、Pass 与 IR:编译器的中间表示结构

什么是 Pass?

Pass 是指编译器在处理源码时的一个阶段性转换逻辑。每一个 Pass 负责某种特定功能,如:

一个现代编译器通常包含数十甚至上百个 Pass。

什么是 IR(Intermediate Representation)?

IR 是介于源代码与机器码之间的一种中间层代码形式。它抽象出平台无关性,同时也适合进行各种优化。常见 IR 类型:

结构示意图:

           High-Level Source Code                     ↓         ┌────────────────────────────┐         │ Lexical → Syntax → Semantic│         │       → IR Generator        │         └────────────────────────────┘                     ↓         Intermediate Representation                     ↓         Target Code Generator (e.g. x86, ARM)                     ↓               Machine Code

IR 的价值在于,它解耦了“源语言”与“目标架构”的强绑定,使得:


四、传统编译器的工作模型

以下是传统编译器的工作示意:

A. 全流程编译模式

Source Code → Compiler → Machine Code → Output                        ↓                  Error Messages(静态检测)

B. 解释器执行模型

Source Code → Interpreter → Output(每一行逐行执行)                     ↺             Get Next Instruction

这体现出传统编译器更关注性能与优化,而解释器关注交互与灵活性。


五、IR 的生成与用途(图示补充)

如图所示:

图示引用:

           High-Level Language                    ↓    ┌────────────────────────────┐    │ Lex → Syntax → Semantic → │    │     Intermediate Generator │    └────────────────────────────┘                    ↓             Intermediate Code                    ↓              Target Code(可执行)

六、小结

概念含义
编译器将源代码转换为机器码的程序
解释器实时读取并执行源代码的程序
JIT即时编译:运行时优化执行路径
AOT提前编译:部署稳定性强,启动快
Pass编译流程中的阶段性处理器
IR编译器内部通用中间表示语言,便于优化与平台适配

💡理解 | 编译器基础概念

🎓 理论理解

在计算机语言的发展历史中,编译器与解释器的划分构成了程序运行机制的两个基本分支:编译器强调静态转换后执行,解释器强调动态逐句执行。这种执行模型的差异决定了它们在不同场景下的适配性。

同时,**JIT 编译(Just-in-Time)**的引入在很大程度上弥合了两者的差距,它保留了解释器灵活性并在运行时动态生成最优机器码,是现代虚拟机(如 JVM、V8)的核心机制之一。

进一步地,IR(Intermediate Representation) 的提出极大推动了编译器模块化和平台无关性的设计理念:通过统一的中间代码层,源语言与目标平台得以解耦,从而实现“一次前端、多后端”策略。LLVM 的成功即建立在强大的 SSA 结构 IR 基础上。

编译器中的 Pass 架构也体现了软件工程模块化设计的思想,每一个 Pass 只承担一个明确职责(词法分析、语义分析、优化等),利于维护、调试与扩展。

🏢 大厂实战理解(华为昇腾 / Google / OpenAI / NVIDIA)

✅ 华为昇腾(Ascend)

本课程正是基于华为昇腾 AI 芯片生态所打造,昇腾平台强调模型部署的性能与可控性,因此:

华为编译器强调部署时的确定性与安全性,因此更依赖 AOT 和静态分析,同时也发展 IR 驱动的图级优化逻辑(如 AutoTune、内存复用)。

✅ Google(TensorFlow / XLA)

Google 的 AI 编译体系构建于 XLA(Accelerated Linear Algebra)编译器之上,强调多阶段 IR 优化:

Google 强调编译器的深度优化能力,通过高层 IR 提供分析信息、图优化,再配合 LLVM 与硬件代码生成,达到高效运行。

✅ OpenAI(训练平台)

OpenAI 内部模型训练平台使用大量容器沙箱化机制,为了确保资源隔离和安全,通常将用户模型定义解释执行,而核心调度、模型下发等操作则通过 AOT 编译代码完成。

✅ NVIDIA(CUDA / TensorRT)

NVIDIA 的编译器体系则完全基于深度优化的 IR 系统构建:


📌 总结一句话

现代编译器不仅是“源代码翻译器”,更是软硬件协同调优的智能调度核心,其底层架构正在从传统静态编译走向“多级中间表示 + 动态执行路径 + 深度优化 Pass”的全栈式智能编译系统。


七、课程思维导图(建议配图)

你可以自行画出如下思维结构:

编译器基础├─ 编译 vs 解释├─ JIT vs AOT├─ Pass / 阶段转换└─ IR:中间表示    ├─ 表达优化    ├─ 平台无关    └─ 后端生成目标代码

🧠 大厂面试题 | 编译器的基础概念


面试题 1:请简要说明编译器与解释器的主要区别,它们适合于哪些类型的语言或场景?

参考回答:
编译器与解释器的核心区别在于代码执行的时机与方式不同:编译器将整个程序在运行前一次性翻译为目标机器代码(AOT),适用于 C、C++、Go 等对性能和类型安全要求高的语言;解释器则在运行过程中逐行翻译和执行源代码,适合脚本语言如 Python、Ruby、JS 等,便于快速开发与交互调试。

在工程实践中,底层驱动程序、嵌入式系统、操作系统内核通常采用 AOT 编译器部署,而数据分析、AI 实验平台和 Web 环境则更偏好解释型语言结合 JIT 优化。


面试题 2:什么是 IR?为什么现代编译器一定需要引入 IR 这一中间表示层?

参考回答:
IR(Intermediate Representation)是介于高级源语言与底层机器代码之间的中间代码形式,它具备平台无关性结构表达性强的优势,使得编译器得以分离前端语言解析与后端代码生成,支持“一次前端、多平台后端”的目标构建模式。

在现代编译体系中(如 LLVM、XLA、TVM),IR 作为优化与调度的中枢,可以以 SSA 结构表达数据依赖关系,便于实现诸如常量折叠、死代码消除、算子融合、调度重排等优化 Pass,是支撑静态编译、JIT 加速与跨平台部署的关键组件。


面试题 3:请对比 AOT 和 JIT 编译的优势与劣势,并说明各自适用于哪些系统场景?

参考回答:
AOT(Ahead of Time)在编译期完成所有代码转换,生成稳定的目标文件,具有启动快、执行快、易于部署的优势,适用于嵌入式系统、系统软件与深度学习推理部署(如 Ascend 离线模型编译)。
JIT(Just in Time)则在运行时根据输入数据与硬件状况动态编译热点路径,具有适应性强、可运行时优化等特点,适合 AI 框架、浏览器引擎、图形渲染等需要动态优化的场景(如 PyTorch JIT、TensorFlow XLA)。

JIT 的劣势是启动慢、内存消耗大;AOT 的劣势则是缺乏运行时上下文信息,优化空间有限。


面试题 4:现代编译器为什么采用多 Pass 架构设计?有哪些工程上的优势?

参考回答:
采用多 Pass 架构是为了将编译器的不同阶段(如词法分析、语法解析、语义检查、优化、代码生成)功能模块化,从而带来以下工程优势:

    职责单一:每个 Pass 只处理一类问题,便于调试与测试;高度复用:通用 Pass 可被多个前端语言或后端平台复用;插拔式扩展:可以动态启用/禁用特定优化逻辑,适应不同部署需求;调度灵活:Pass 顺序可根据目标平台进行调整,实现渐进式优化(如 TensorRT 的 kernel fusion Pass)。

例如 LLVM PassManager 支持动态注册 Pass 队列,MindSpore GraphEngine 中的优化器也以 Pass 链方式构建模型图优化管线。


面试题 5:假设你现在要设计一个编译器框架来支持 C/C++ 和 Python 两种语言,你会如何组织前端、中间表示与后端架构?

参考回答:
我会采用多前端、统一 IR、多后端的三层编译结构:

    前端:使用 Clang 处理 C/C++ 源码,使用 Python AST 分析器处理 Python 代码,统一输出中间表示;IR 层:设计一个基于 SSA 的中间表示(如 LLVM IR),用于表达控制流、数据流与操作符;后端:根据目标平台分别生成 x86、ARM、CUDA、Ascend Kernel 等目标指令码;调度器:插入 Pass 管理器,支持静态优化、算子融合、图调度等通用处理。

这种架构可参考 LLVM、TVM 和 MindSpore 的设计思路,实现高扩展性与跨语言支持。

场景题 1:

你在昇腾团队负责图算融合优化的编译子系统,现在遇到一个模型在进行 IR Pass 时生成的中间表示图结构冗余,导致 Kernel 拆分严重,性能下降明显。你如何定位并优化这个问题?


参考答案:

在面对图结构冗余导致算子拆分与性能下降这一问题时,我首先不会直接去调试后端代码生成逻辑,而是选择从中间表示构图的前链入手,具体会在 IR Dump 阶段开启详细的结构日志,结合 Graph Visualizer 或 IR Viewer 工具,从图的构建初期开始逐层追踪节点生成与流转路径,以定位哪些节点是冗余添加、哪些是重复计算,尤其是关注常见的未融合算子模式如 Broadcast→Cast→ReshapeTranspose→MatMul 等链式结构是否被错误拆解。接着,我会结合当前 FusionPass 执行的调度顺序审视是否存在优化 Pass 执行次序不当的问题,例如是否先执行了形状推理 Pass 导致图结构提前展开,从而影响后续的算子融合逻辑失效。针对这一点,我会尝试在 Pass Manager 中重排图优化顺序,把与算子融合相关的 FusionPass 提前到图拓扑稳定后再执行,从而提升融合率。

与此同时,我还会重新检查当前 Fusion 模板的匹配逻辑是否过于保守,比如当前模板是否只允许 Conv→BN→ReLU 严格连续而未考虑 BN 被转化为 Scale+Shift 形式后导致融合失败,此时我会扩展 Pattern Engine 的匹配规则,允许更灵活的图模式识别。同时,在中间表示被构建之前,如果模型导出源是 Python 脚本或 ONNX、MindIR 等图描述语言,我也会从源端对原始图进行清洗,移除显式保留但实际无效的 IdentityNoOp 操作,减少对后续 IR 图的污染。

最终,在完成融合规则调整、Pass 调度重排以及 IR 构图源头清洗后,我会通过重新编译和运行模型,观察 kernel 拆分数量是否减少、Launch 次数是否降低,并结合 Profiling 工具验证推理延迟是否下降。在一次真实场景中,我通过上述优化使得某视频超分模型的 kernel 数从原来的 280 降至 170 左右,整体推理延迟下降了超过 15%,显著提升了昇腾端到端部署的效率与能耗表现。


Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

编译器 解释器 JIT AOT IR
相关文章