news 2026/9/7 5:44:14

AI编译器入门路线:从LLVM到TVM/MLIR的完整实践指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI编译器入门路线:从LLVM到TVM/MLIR的完整实践指南

最近两年,"AI编译器"这个词在技术圈的热度一路走高。我身边不少做深度学习框架、系统优化甚至后端开发的朋友都在问同一个问题:这个东西到底该怎么入门?网上的资料要么是论文级别的大部头,要么是纯打广告的培训课,真正能一条线串起来、照着就能下手的学习资料少之又少。

我当时转AI编译器方向的时候,也走过不少弯路。拿到一份LLVM代码不知道从哪看起,读了三天TVM源码感觉自己什么都懂了,一关掉编辑器又什么都想不起来。后来靠着一个一个项目磨、一遍一遍过IR dump,才算把这条路的骨架摸清楚。所以这篇东西我就直接把我认为最靠谱的学习路线、项目清单、以及每个环节真正需要注意的坑,一次性整理出来。不管你是刚毕业的学生,还是从应用层转底层优化,这篇文章的思路都可以直接用。

1. 先想清楚:AI编译器到底解决的是什么问题

很多初学者一上来就抱着TVM或MLIR的源码啃,结果越看越迷糊。根本原因是你不知道这些代码服务于什么目标,自然就看不懂它为什么这么设计。所以在碰任何代码之前,先把AI编译器的定位和核心问题搞明白。

1.1 为什么传统编译器不适用于深度学习场景

传统编译器(比如GCC、LLVM)解决的核心问题是:把高级语言翻译成目标机器指令,同时做各种优化。它的输入是C/C++这种通用语言,输出是CPU或GPU的机器码,优化目标是让程序运行得更快、代码更小。

但深度学习推理和训练场景有一些完全不同的特点:

  • 计算模式高度固定。深度学习模型本质上是一张大计算图,图中的节点是矩阵乘、卷积、归一化、激活函数这些算子,边是张量数据流。这个计算图在执行前就已经完全确定了,不需要像通用程序那样面对无穷多的控制流分支。
  • 性能瓶颈在数据搬运和访存模式。深度学习算子在GPU上跑得慢,通常不是因为ALU利用率不够,而是因为数据从全局内存搬到共享内存、再从共享内存搬到寄存器这个过程中出现了大量等待和浪费。这跟传统编译器主要关心指令选择和寄存器分配是很不一样的。
  • 目标硬件高度异构。NVIDIA的GPU、AMD的GPU、华为的昇腾、各种NPU,每个硬件都有自己的内存层次结构、并行模型和指令集。同一个卷积,在这张卡上要用implicit GEMM的写法,在另一张卡上可能用Winograd更快。传统编译器针对的是相对统一的指令集架构,而AI编译器面对的是碎片化的加速硬件生态。

AI编译器做的事情,简单说就是:给定一个模型描述和一块目标硬件,自动生成在这块硬件上最高效的可执行代码。它把"模型"而不是"语言"作为编译输入,把"算子的高效实现"而不是"通用指令选择"作为核心优化目标。

1.2 AI编译器的三层典型架构

虽然不同框架的AI编译器实现各不相同,但整体架构高度相似,通常分为三层:

层次典型组件作用
前端PyTorch的TorchScript/TorchDynamo、TensorFlow的Grappler、ONNX把训练好的模型转换成计算图,做图级优化(算子融合、常量折叠、死代码消除)
中间层TVM Relay、MLIR的多种Dialect、XLA HLO对计算图做硬件无关的优化,把图降低为更底层的中间表示
后端LLVM、NVCC、各种Codegen把中间表示转换成目标硬件的机器码或者可调用的算子库

初学者脑子里装上这个三层架构,再去看任何AI编译器代码,心里就有了一张地图:你现在看到的这段代码,到底是在做前端的图变换,还是中端的IR优化,还是后端的代码生成。定位清楚,阅读效率能提升一倍。

2. 打地基阶段:三门必修课和一条贯穿主线

很多人问我:做AI编译器需要把编译原理全部学完吗?我的答案是:不需要啃完龙书,但也有绕不开的底线。下面这三块内容是必修,缺一块后面都会卡壳。

2.1 硬件体系结构:你是在跟机器打交道

AI编译器最终生成的代码要跑在GPU/NPU上,所以你必须清楚这些硬件的执行模型。这不是嵌入式系统那种"了解寄存器"的级别,而是要理解到"看到一个算子就能大致估算出性能瓶颈在哪"的程度。

以NVIDIA GPU为例,最需要掌握的几个核心概念:

  • 线程层次:grid、block、warp,以及它们如何映射到SM(流式多处理器)上;
  • 内存层次:全局内存、共享内存、寄存器、常量内存各自的容量和延迟差异。共享内存通常只有几十KB到上百KB,全局内存延迟却有几百个周期。一个算子优化得好不好,关键看你有没有把全局内存的复用变成共享内存的复用;
  • Tensor Core:从Volta架构引入,专门做矩阵乘加运算。fp16、bf16、int8这些低精度类型配合Tensor Core,吞吐量可以远超普通CUDA Core;
  • SIMT执行模型与warp divergence:一个warp里的线程要执行同一指令,分支分叉会导致性能骤降。

推荐资源:NVIDIA的CUDA C++ Programming Guide里的"Programming Model"和"Memory Hierarchy"两章是必读的。另外可以去看陈天石的GTC演讲或者各种GPU体系结构教程,对建立硬件直觉很有帮助。

判断标准:如果你看到一篇优化卷积的文章里说"把im2col改成implicit GEMM以减少内存开销",你能立刻理解它在说什么,这关就算过了。

2.2 编译原理核心:抓住IR和Pass这两个命门

传统编译原理里面对AI编译器最有用的部分,不是词法分析,也不是自顶向下的语法分析,而是中间表示(IR)的设计Pass基础设施

AI编译器的优化本质上就是一趟一趟的Pass:分析IR,发现某种优化机会,改写IR,得到新的IR。MLIR这个框架把这个思想推向了极致——它定义了一整套可扩展的IR基础设施,允许不同抽象层级用不同的Dialect来表达。

学这一块我推荐两条路径并行:

  1. 读LLVM的官方文档中关于IR的部分(特别是LangRef的前半部分),搞清楚什么是basic block、什么是dominance、什么是use-def链;
  2. 去看一下MLIR toy tutorial(Chapter 1到Chapter 5),这是MLIR官方提供的入门教程,用Toy语言把MLIR的Dialect、Pass、Lowering全走了一遍,做完之后你会对"If you're building an IR, you're building a compiler"这句话有具身理解。

我不建议一上来就啃龙书的词法分析那一章,对AI编译器方向性价比太低。

2.3 深度学习框架基础:要知道模型长什么样

AI编译器处理的是模型,所以你必须能看懂模型代码,理解张量的shape、dtype、layout和算子的语义。不需要你会手写ResNet训练,但至少要能做到:

  • 读懂PyTorch模型定义里forward函数的数据流;
  • 知道什么是静态shape、什么是动态shape,哪些算子是有状态的(比如BatchNorm的running_mean);
  • 了解Tensor的stride和memory format(NCHW/NHWC)对算子性能的影响。

这些知识在后续做图优化和算子优化时会频繁用到。比如你看到一个FoldScale的pass,如果不理解BatchNorm推理时可以把scale和bias融合进卷积的权重,你就无法理解这个pass为什么要存在。

2.4 贯穿始终的主线:计算与访存的平衡

如果说有一个思维模式是AI编译器干活的核心主线,那就是一句话:计算要并行化,访存要局部化

为什么这么讲?GPU有几千个核心,算力很强,但数据搬运是瓶颈。一个矩阵乘法,理论上需要执行的浮点运算次数是固定的,你能优化的是:相邻线程访问的地址是否连续(合并访存)、数据是否能在共享内存中复用(减少全局内存访问)、指令流水线是否被计算密集型指令占满(隐藏访存延迟)。

学AI编译器的时候,每接触一个优化技术,都可以问自己两个问题:它是让计算更并行了吗?还是让访存局部性更好了?绝大多数优化都能归到这两类中的一类。有了这条主线,你学到的各个零散知识点才能串起来。

3. 项目清单:从造轮子到读源码,六个项目循序渐进一步一个脚印

学习编译器这类工程性极强的方向,光看书是绝对不行的。项目是检验你理解程度的唯一标准。我把做过的和带人做过的项目整理成下面这个清单,从零基础到进阶分成了六个项目,每个项目都有明确的目标和验收标准。

3.1 项目一:手写一个微型C语言子集编译器(2-3周)

这个项目听起来跟AI编译器没关系,但它练的是编译器的核心基本功:流水线。输入是一个简化版C语言(比如只有int类型、加减乘除、赋值、函数调用),输出可以是LLVM IR或RISC-V汇编。

具体任务拆解:

  1. 写词法分析器:把源代码切成token流;
  2. 写递归下降语法分析器:识别表达式、语句、函数定义;
  3. 生成AST(抽象语法树);
  4. 写一个简单的IR生成器,输出LLVM IR;
  5. 调用clang/llc把IR编成可执行文件,跑通测试用例。

这个项目最大的价值在于,你会亲身体会到IR作为"前后端分界线"的意义:前端只需要生成合法的IR,剩下的所有优化和代码生成都由后端完成。做完之后你再去看TVM里的Relay IR,会有一种"这东西我见过"的亲切感。

参考资源:国内很多高校的编译原理课程设计都有类似题目。也可以用llvm的Kaleidoscope教程入门,它的语言是一个简单的函数式语言,但麻雀虽小五脏俱全,从Lexer到JIT都有。

3.2 项目二:用TVM为自定义算子生成CUDA核函数(3-4周)

这是从"编译器理论"跨到"AI编译器实践"的关键一步。目标是:不手写CUDA Kernel,而是用TVM的TE(Tensor Expression)描述一个算子,让TVM生成可在GPU上运行的代码。

建议从GELU(高斯误差线性单元)这种简单算子开始,然后做LayerNorm,最后挑战一个带reduction的算子(比如softmax)。

核心步骤:

import tvm from tvm import te # 以softmax为例,先用TE描述计算逻辑 n, m = te.var("n"), te.var("m") A = te.placeholder((n, m), name="A") A_max = te.reduce_max(A, axis=1, keepdims=True) A_exp = te.exp(A - A_max) A_sum = te.sum(A_exp, axis=1, keepdims=True) B = te.compute((n, m), lambda i, j: A_exp[i, j] / A_sum[i, j])

写完TE描述之后,用schedule做优化:先加向量化,再做线程绑定,然后分析生成的CUDA源码,对比手动实现版本的性能差异。

这个项目练的是"算子即程序"的思维:一个算子的实现不是死的,而是可以通过调度策略改变的。你会深刻理解为什么"计算定义"与"调度"分离是TVM对深度学习编译器最大的贡献之一。

3.3 项目三:分析并改掉一个TVM/MLIR的Bug(4-6周)

这个项目要求你主动去GitHub的issue区找一个AI编译器相关的bug,理解它、修复它、提交PR。

为什么这个项目价值极高?因为你在写代码的时候,无论读了多少源码,都只是"被动理解"。但修bug要求你主动定位:一个问题产生了,它是在前端被错误转换了?还是中端IR改写时有语义差异?还是后端的代码生成没覆盖某个边界条件?

我记得自己修过的第一个bug是TVM里某个算子schedule在特定shape下的错误绑定。排查过程花了两周,但这两周我几乎把schedule绑定的源码读了个遍,对TVM的认知深度远超之前三个月的泛读。

> 操作建议:不要挑那种一眼就能看出问题的bug,选那种附带了完整issue描述和复现步骤的。在没有把握之前,先在issue里回复自己的分析思路,维护者有时候会给你非常有价值的提示。

3.4 项目四:从零搭建一个微型算子编译器(6-8周)

这个项目是进阶路上的硬骨头——用LLVM的框架搭一个只服务于少数几个算子的小型编译器。输入是一个简化的计算图描述(JSON就行),输出是LLVM IR,再通过LLVM生成可执行代码。

关键任务:

  1. 定义一个简单的DSL(可以直接用JSON或Python,不用重新发明语言);
  2. 对每个算子(矩阵乘、卷积、ReLU、Add)编写Lowering逻辑,转成LLVM IR;
  3. 接上优化Pass(比如循环不变量外提);
  4. 把生成的代码跑在CPU上,和对拍库(如Eigen)对比,推导正确性。

这个项目的价值在于,它会逼着你把"AI编译器的骨架"亲手搭一遍。你会发现TVM/MLIR里很多看似抽象的设计,其实都是你这个小编译器面临的问题在更大规模下的映射。

从项目三到项目四,是一道分水岭:项目三让你读懂别人的编译器,项目四让你能写自己的编译器。能完成项目四的人,基本就已经具备做AI编译器研发的能力了。

3.5 项目五:用MLIR实现一个自定义Dialect和Pass(6-8周)

MLIR是当前AI编译基础设施的事实标准,不会MLIR的AI编译器工程师,在市场上竞争力会大打折扣。项目四如果已经让你对LLVM有了良好的手感,项目五就是专门练MLIR的。

任务设计:

  1. 定义自己的Dialect(比如叫toy),规定它的Operation、Type、Attribute;
  2. 实现从你自己的DSL到toyDialect的Lowering;
  3. 实现若干个Pass,比如"把一个toy.matmul+toy.add的pattern融合成一个toy.fused_matmul_add";
  4. 实现从toyDialect到LLVM Dialect的Lowering;
  5. 用MLIR的单元测试框架给pass写测试。

这个项目做完之后,你对MLIR的"多级IR"、"Dialect间显式Lowering"、"基于Pattern的Rewrite"这三个核心思想会有很直观的感知。

3.6 项目六:为一个真实加速器写一个Codegen后端(10周以上)

这是终极项目,也是AI编译器工程师日常工作的真实写照:你手里的加速器有自己的指令集,但没有编译器,你的任务是把某种IR翻译成这个指令集。

如果手边有NPU开发板当然最好,没有的话可以考虑用CPU的SIMD指令集或者RISC-V的向量扩展(RVV)来模拟。核心是:输入是某种中间表示,输出是目标平台可执行的指令序列。

这个项目练的是全栈能力。你会遇到TVM里CodeGenSourceModule的注册机制、寄存器分配器的实现、指令调度的顺序等等。能从头到尾跑通一个端到端demo(模型进来,调优代码出去,性能还不差),就已经具备AI编译器方向的硬实力了。

4. 避坑指南与实用技巧

AI编译器的资料确实多,但正因为多,"不知道怎么选、怎么读"反而成了最大的门槛。下面这几点是我踩过的坑,希望你能绕开。

4.1 不要一上来就看源码,要从顶层设计向下看

这个问题我见过太多次了。初学者特别喜欢把TVM或MLIR的仓库clone下来,然后从main函数开始一行一行读。结果就是代码里各种Visitor、Pattern、Dialect来回跳,一周下来脑子变成一团浆糊。

正确方式是"从数据结构入手,以IR为主轴":

  • 先弄清楚这个编译器里有哪几种IR(比如TVM里的Relay IR和TIR,MLIR里不同Dialect);
  • 再看这些IR之间的转换关系(哪个pass把高层IR降低到低层IR);
  • 最后才需要去关注某个具体的pass实现细节。

我自己读TVM源码时的路径是:先跑一个端到端的例子,用print(relay_ir.astext())把IR打印出来,对照着IR结构去源码里找对应的类定义。IR是编译器的"用户界面",先把用户界面摸熟,内部实现就只是时间问题了。

4.2 不可盲目追新,官方tutorial的版本匹配很重要

TVM、MLIR这种项目发展速度极快,API经常变动。你网上搜到一篇三年前的博客,里面的代码放到现在的版本几乎肯定跑不起来。

所以当你决定要用某个框架做项目时,第一件事就是去它的官方仓库把版本固定住,然后找相同版本的文档和tutorial。不要贪心用master分支,也不要Windows下的Visual Studio编译,直接在Linux环境里搞定最简单。

> 注意:MLIR的toy tutorial是跟着LLVM主分支走的,API变动非常频繁。如果发现代码编译不过,优先去查LLVM的release note,看这个接口在哪个版本改掉了。

4.3 手写算子对比是验证理解最好的方式

这个建议主要针对项目二、项目三。很多人用TVM生成了一段CUDA代码跑通之后,就觉得自己会写算子编译器了。这是错觉。

我建议你在TVM之外,再用手写一个CUDA kernel实现同样的算子,然后用NVIDIA Nsight或者简单的计时函数去对比性能:

  • 如果手写版本更快,去看TVM生成的代码差距在哪;
  • 如果TVM版本更快,去看它的schedule为什么更优。

这个过程会逼着你把"生成代码"和"手写优化"两边的知识都补齐。能说清楚"某个性能差异是为什么产生的",才说明你真正理解了优化策略。

4.4 阅读Meetup记录和大会Talk,建立产业全局观

AI编译器是一个快速的产业。每年LLVM Developer Meeting、CGO、MLSys等会议都有很多前沿的实践分享。这些Talk通常由一线工程师来讲,比论文更贴近工程落地。比如去听一下PyTorch团队讲TorchInductor的设计演进,你就能知道Meta内部对"编译派 vs 算子派"路线之争的取舍到底是什么。

养成在跑步、通勤时用听Talk的方式积累产业动态的习惯,对面试、选方向、甚至判断技术趋势都很有帮助。

5. 学习时间规划:四个月从入门到可上手

下面是我觉得比较现实的四个月周期安排,按每周大约15-20小时的学习量来算:

阶段时间重点内容完成标志
基础期第1-4周硬件体系结构、编译原理核心、PyTorch基础;动手跑完项目一理解GPU执行模型;知道IR是编译器的分水岭
框架期第5-8周完成项目二;选一个框架(TVM或MLIR)精读其tutorial和核心源码能独立为自定义算子生成代码;能流畅讲出TVM/MLIR的架构分层
深潜期第9-12周完成项目三(修bug)或项目四(微型编译器)能定位并修复一个真实issue;或者能写出一个能跑的小型编译器
进阶期第13-16周走MLIR相关项目(项目五或项目六)能独立开发一个Dialect并实现Pass

时间只是一个参考。关键是每一阶段的目标必须可验证,比如"能跑通""能修掉""能写出来",而不是"看了一遍"。宁可慢一点,不能囫囵吞枣。

在这个行业里,"看上去会了"和"真的会了"之间的差距,往往就是这些可验证产物的差距。模块化恐惧症没有用,动手打开编辑器才是唯一的解药。

最后分享一个最近在带新人的时候常用的小技巧:把TVM的Relay IR里认为最晦涩的pass挑出来(比如FoldScaleAxis),先用一手资料搞清楚它做了什么,然后在代码里加上日志,亲自跑一个例子观察IR变化,最后用一句大白话把自己忽悠明白——如果这句话能让你旁边的人听懂,你才是真的懂了。这个方法对所有AI编译器框架都适用。希望这份清单能帮你少走一些弯路,四个月后,我们可能在同一个IRC频道见。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 5:43:33

用吴麒课件自学自动控制原理:从建模到校正的完整闭环

简介:吴麒版《自动控制原理》课件是一份面向自动化、电子工程、航空航天等专业学生及考研、自学者的系统学习资料,以经典教材为蓝本,深入讲解控制系统建模、稳定性分析、频域响应、控制器设计与系统校正等核心内容。压缩包为RAR格式&#xff…

作者头像 李华
网站建设 2026/9/7 5:43:26

AGI时代前端工程师的核心竞争力:判断力与责任不可替代

最近在技术社区刷到 Yuchen Jin 关于 AGI 的那番讨论,说实话,第一反应是松了口气。大家都在喊 AI 要接管一切的时候,有人站出来说"该做的活一样都跑不掉",这话听着就踏实。他的观点其实不复杂:AGI 很强&…

作者头像 李华
网站建设 2026/9/7 5:42:04

CUDA编程与TensorRT加速:深度学习模型部署优化实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 5:42:01

Vibe Coding实战:别迷信Prompt,关键在四件事

最近 Vibe Coding 这个词在开发者圈子里刷屏刷得厉害。原本我以为又是一阵短暂的热度,结果身边的朋友一个接一个真香——用自然语言描述需求,让 AI 把代码直接怼出来,这种写代码的方式确实改变了一大批人的工作习惯。但我发现一个比较明显的误…

作者头像 李华