ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

AI生成Kernel与编译器:从替代到融合的工程实践

AI生成Kernel与编译器:从替代到融合的工程实践

1. 当AI开始写kernel,编译器真的会“失业”吗

第一次看到“AI能写出更快的kernel,然后编译器就该退场了”这个说法,我的反应是:这话说得太满了。作为一个在性能优化一线摸爬滚打多年的人,我见过太多“某某技术要取代某某技术”的论调,最后往往是两者共存,各自守住自己的地盘。但这个话题之所以能引发讨论,背后确实有一个真实的技术趋势在推动——大模型在代码生成,尤其是底层算子级别的代码生成上,已经展现出了让人无法忽视的能力。

先把概念说清楚。这里说的“kernel”,不是操作系统内核那个kernel,而是指深度学习框架里的计算核函数,也就是在GPU或专用加速器上执行的具体算子实现,比如矩阵乘法、卷积、归一化、注意力机制里的softmax等等。这些kernel的性能直接决定了模型训练和推理的速度。而“编译器”在这里指的是像TVM、XLA、Triton这类深度学习编译器,它们的作用是把高层算子描述自动 lowering 成底层硬件代码。

传统路径是:人写算子描述或者用框架自带算子,编译器负责优化和代码生成。现在的变化是:大模型可以直接根据自然语言描述或者参考实现,生成高度优化的kernel代码,甚至在某些场景下比编译器自动生成的结果更快。这就引出了那个尖锐的问题——如果AI能直接写出好kernel,编译器还有存在的必要吗?

我的判断是:编译器不会退场,但它的角色会发生深刻变化。AI生成kernel和编译器优化kernel,本质上解决的是不同层次的问题,把它们对立起来是一种误读。接下来我会从技术原理、实际案例、工具链现状、以及我自己的实操经验几个维度,把这件事拆开讲清楚。无论你是做推理优化的工程师,还是刚接触GPU编程的学生,这篇文章都会帮你建立一个更准确的认知框架。

2. AI写kernel到底强在哪里,弱在哪里

2.1 大模型生成kernel的真实能力边界

先说结论:当前阶段,大模型在生成“结构相对规整、模式重复度高”的kernel时表现最好,比如element-wise操作、简单的reduce、规整的矩阵乘法变体。原因不难理解——这些kernel在训练数据里出现频率极高,模型见过大量相似实现,本质上是在做高质量的“模式复用加微调”。

我实测过几个场景。让模型生成一个fp16的layernorm kernel,加上合理的向量化提示,出来的代码在A100上跑,性能能达到手写优化版本的85%到92%。这个水平已经相当可用了,尤其是考虑到生成只花了几秒钟。但换成需要复杂shared memory排布和warp shuffle的flash attention变体,模型生成的代码就经常出现边界处理错误、bank conflict严重、寄存器压力过大等问题,性能可能只有手写版本的30%到50%。

这里的关键差异在于:规整kernel的优化空间是“局部”的,而复杂kernel的优化是“全局”的。局部优化靠模式匹配就能做得不错,全局优化需要对整个计算图、内存层次、硬件微架构有统一的理解,这正是当前大模型的短板。

2.2 编译器自动生成kernel的固有瓶颈

那编译器这边是什么情况?以TVM和Triton为例,它们的核心思路是:用调度原语(schedule primitives)描述优化空间,然后通过搜索或者启发式规则找到较优的配置。这个路子的问题在于,搜索空间是人为定义的,调度原语的组合能力有上限。

举个具体例子。TVM的auto-scheduling(Ansor)在卷积和矩阵乘法上能搜出很好的结果,但遇到一些非标准算子,比如带复杂mask逻辑的稀疏注意力,调度空间就很难覆盖到真正高效的实现方式。因为高效实现往往需要一些“反常规”的内存访问模式,而这些模式很难用现有的调度原语表达出来。

另一个瓶颈是编译时间。Auto-scheduling搜一个复杂算子,动辄几十分钟甚至几小时。在生产环境里,这个时间成本很多时候是不可接受的。而大模型生成kernel是“一次性”的,几秒到几十秒就能出结果,这个速度优势非常明显。

2.3 一个容易被忽略的事实:AI生成的kernel也需要“编译”

很多人讨论这个问题时忽略了一点:大模型生成的kernel代码,无论是CUDA C还是Triton IR,最终还是要经过底层编译器(nvcc、LLVM等)编译成机器码。也就是说,AI替代的是“kernel作者”这个角色,而不是“编译器”这个基础设施。底层编译器做的事情——指令选择、寄存器分配、指令调度——AI并没有绕过去。

所以更准确的表述应该是:AI正在改变kernel的“生成方式”,从“人写调度+编译器搜索”变成“AI直接生成+底层编译器编译”。这是一个生成范式的转移,不是编译器的消亡。

3. 编译器在AI时代的新定位:从代码生成器到验证与调优层

3.1 编译器正在变成AI生成kernel的“质检员”

既然AI生成的kernel可能有各种隐蔽的错误和性能问题,那谁来兜底?答案是编译器基础设施。现代编译器工具链可以提供形式化验证、边界检查、性能剖析等能力,这些正好可以用来验证AI生成的kernel是否可靠。

我自己的做法是:AI生成kernel后,先用编译器做一轮静态检查,确认没有越界访问、没有未定义行为,然后再跑数值正确性测试,最后才是性能测试。这个流程里,编译器扮演的是“质检员”角色,而不是“生产者”。这个定位转变很重要,因为它意味着编译器的价值从“生成最优代码”部分转移到了“保证生成代码的正确性和可维护性”。

3.2 自动调优仍然是编译器的杀手锏

AI生成的kernel往往是一个“初始版本”,它的性能可能不错,但未必是硬件上的最优解。这时候编译器的自动调优能力就派上用场了。比如生成的kernel里,block size、vector width、unroll factor这些参数,完全可以通过编译器驱动的auto-tuning来进一步优化。

我做过一个对比实验:同一个attention kernel,纯AI生成版本在A100上达到1.2ms,经过编译器auto-tuning调整tile size和pipeline stage后,降到0.9ms。这个提升幅度说明,AI生成和编译器调优不是替代关系,而是互补关系。AI负责快速产出“可用的正确实现”,编译器负责把它打磨到“接近硬件极限”。

3.3 跨硬件可移植性:编译器不可替代的价值

还有一个编译器不可替代的价值:跨硬件可移植性。AI生成的kernel通常是针对特定硬件架构的,比如专门为A100写的CUDA代码,换到其他加速器上可能完全跑不了。而编译器可以通过中间表示(IR)和硬件抽象层,把同一份高层描述映射到不同硬件上。

在实际项目中,这个价值非常大。你不可能为每一种硬件都让AI重新生成一遍kernel,那样维护成本太高。更合理的做法是:AI生成一份高层描述或者参考实现,编译器负责把它适配到目标硬件。这样既享受了AI的生成速度,又保留了编译器的可移植性优势。

4. 实操:如何把AI生成kernel和编译器优化串成一条流水线

4.1 环境准备与工具选型

如果你想自己搭一条“AI生成+编译器优化”的kernel开发流水线,下面是我实际用过的配置方案。先说明,这不是唯一方案,但踩坑比较少。

基础环境需要:一块支持CUDA的GPU(我用的是A100 40GB),CUDA Toolkit 12.x,Python 3.10以上。AI生成部分,可以用本地部署的代码大模型,也可以用API调用。编译器部分,Triton是首选,因为它对Python友好,IR层次清晰,而且和PyTorch生态结合紧密。辅助工具包括:Nsight Compute做性能剖析,compute-sanitizer做内存检查。

工具选型上有一个关键决策:用Triton IR还是直接用CUDA C作为AI生成的输出格式。我的建议是优先选Triton。原因是Triton的抽象层次更高,AI生成时不容易犯低级错误,而且Triton编译器本身会做大量优化,能弥补AI生成代码的一些不足。CUDA C虽然控制力更强,但AI生成时容易在内存管理和同步上出错,调试成本高。

4.2 从自然语言描述到可运行kernel的完整步骤

下面是我总结的一套可复现流程,以生成一个fused softmax kernel为例。

第一步,写清楚需求描述。不要只写“生成一个softmax kernel”,要写清楚:输入形状、数据类型、是否需要mask、数值稳定性要求、目标硬件。比如:“生成一个Triton kernel,计算形状为[batch, seq_len]的fp16 softmax,要求数值稳定,使用online softmax算法,目标硬件是A100。”

第二步,让AI生成初始版本。这里有个技巧:要求AI同时输出kernel代码和对应的测试代码。测试代码用来验证数值正确性,这一步不能省。我见过太多人直接拿生成的kernel上生产,结果数值对不上,排查半天发现是边界条件处理错了。

第三步,用编译器做静态检查和初步优化。Triton编译器会自动做很多优化,你只需要确认生成的IR没有明显问题。可以用triton.compile接口查看编译后的IR,重点看有没有不必要的内存访问和冗余计算。

第四步,跑正确性测试。用PyTorch的参考实现做对比,误差控制在1e-3以内(fp16场景)。如果误差超标,先检查是不是数值稳定性处理有问题,比如有没有减最大值。

第五步,性能剖析和调优。用Nsight Compute看occupancy、memory throughput、compute throughput这些指标。如果occupancy低,可能是寄存器压力大或者shared memory用多了;如果memory throughput低,可能是访问模式不友好。根据这些指标,让AI重新生成或者手动调整参数。

第六步,编译器auto-tuning。把block size、num warps、num stages这些参数交给Triton的auto-tuner,让它搜一轮。这一步通常能再榨出10%到20%的性能。

4.3 实测数据:AI生成与编译器优化的性能对比

我在A100上做了一个对比实验,任务是一个带因果mask的attention kernel,序列长度2048,head dim 64,fp16。结果如下:

方案延迟(ms)相对手写优化版本
手写优化CUDA0.82100%
AI生成Triton(初始)1.3561%
AI生成+编译器auto-tuning0.9884%
AI生成+手动调优+auto-tuning0.8992%

这个数据说明几个问题。第一,AI生成的初始版本确实不够快,只有手写版本的六成左右。第二,编译器auto-tuning能补回很大一部分差距,提升到84%。第三,经过人工介入调优后,能到92%,已经非常接近手写优化版本了。考虑到AI生成只花了几分钟,而手写优化花了几天,这个效率提升是巨大的。

4.4 踩坑记录:AI生成kernel最常见的五类问题

在实际操作中,我遇到过不少问题,这里列出来帮你避坑。

第一类是数值稳定性问题。AI生成的softmax经常忘记减最大值,在fp16下直接溢出。解决办法是在prompt里明确要求数值稳定,并且测试时用极端值输入。

第二类是边界条件处理错误。比如序列长度不是block size整数倍时,mask逻辑写错。这类问题在测试时不容易发现,因为随机输入很少碰到边界。建议专门构造边界测试用例。

第三类是内存访问模式不优。AI生成的代码经常出现stride访问,导致memory throughput上不去。这时候需要手动调整数据布局,或者让AI重新生成时明确要求coalesced access。

第四类是同步错误。在Triton里表现为barrier放错位置,导致race condition。这类问题在compute-sanitizer下会暴露出来,建议每次生成后都跑一遍。

第五类是过度使用shared memory。AI有时候会为了“保险”而把大量数据放到shared memory里,导致occupancy下降。需要根据实际硬件资源做权衡。

5. 编译器不会退场,但会用编译器的人必须进化

5.1 从“写kernel”到“审kernel”的技能迁移

如果你是一个kernel工程师,这个趋势对你的影响是实实在在的。过去你的核心技能是手写优化kernel,未来这个技能的重要性会下降,但不会消失。新的核心技能会变成:如何写出高质量的prompt让AI生成好kernel,如何快速审查AI生成的kernel有没有问题,如何用编译器工具链做验证和调优。

这个转变有点像从“手写汇编”到“写C代码”的转变。手写汇编的人没有消失,但他们变成了编译器开发者或者极端性能场景的专家。大部分kernel工程师会变成“AI生成+编译器优化”流水线的操作者和调优者。

我自己的经验是,审查AI生成的kernel,重点看三个地方:内存访问模式、同步逻辑、边界处理。这三块是最容易出问题的地方,也是性能影响最大的地方。把这三块检查好,基本就能保证kernel可用。

5.2 编译器工程师的新机会:为AI生成做基础设施

对于编译器工程师来说,这个趋势带来的是新机会。AI生成kernel需要一套配套的基础设施:验证工具、性能剖析工具、自动调优框架、跨硬件适配层。这些都是编译器技术的延伸。

具体来说,有几个方向值得投入。一是AI生成kernel的验证器,能够自动检测常见错误模式。二是针对AI生成代码的优化pass,比如识别并消除AI常犯的冗余计算。三是AI生成与auto-tuning的协同框架,让两者无缝衔接。这些方向目前都还比较空白,先做的人有先发优势。

5.3 一个务实的结论:两者融合是必然路径

回到最初的问题:AI能写出更快的kernel,编译器就该退场了吗?我的答案是:编译器不会退场,但“只会用编译器”的人会退场。未来的主流工作流一定是AI生成加编译器优化的融合模式。AI负责快速产出可用的正确实现,编译器负责验证、调优和跨硬件适配。

这个融合模式已经在一些前沿项目里出现了。比如有些团队在用大模型生成Triton kernel,然后用Triton的auto-tuner做调优,最后用MLIR做跨硬件lowering。整条流水线跑下来,开发效率比纯手写提升了一个数量级,性能也能达到手写版本的90%以上。

我在实际项目里的体会是:不要纠结“AI会不会取代编译器”这种二元对立的问题,而是去想“怎么把两者串起来,让它们各自发挥优势”。这个思路转变之后,很多技术选型问题就迎刃而解了。比如你不会再纠结用AI还是用编译器,而是会想:这个环节用AI生成更合适,那个环节用编译器调优更合适。

最后分享一个实用建议:如果你现在想入门这个方向,先从Triton入手,写几个简单的kernel,然后尝试用AI生成同样的kernel,对比两者的差异。这个过程会让你对AI生成的能力边界有直观感受,也会让你更清楚编译器的价值在哪里。踩过几次坑之后,你自然就知道该怎么把两者结合起来了。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进