1. 从"手写Kernel"到"Agent自动调优":这个方向为什么突然火了
如果你在GPU高性能计算这个圈子里待过几年,会发现一个很有意思的转折点。大概在2023年之前,聊到CUDA Kernel优化,大家默认的路径是:人肉读PTX/SASS、翻Nsight Compute的指标、一行行调shared memory的bank conflict、反复试tile size。一个成熟的Kernel工程师,调一个GEMM或者Attention Kernel,花上几天甚至几周是家常便饭。但从2024年下半年开始,越来越多的团队开始尝试让大模型Agent来接管这个流程——不是简单地"生成一段CUDA代码",而是让Agent具备"写代码→编译→跑benchmark→读profiling→改代码"的闭环能力。
这就是"CUDA Kernel Agent"这个方向的核心。它要解决的不是"能不能生成CUDA代码"这种初级问题,而是"能不能让一个自动化系统在无人干预的情况下,把一个Kernel的性能推到接近甚至超过资深工程师手调的水平"。关键词里的CUDA Kernel、Agent、自动调优、LLM for HPC、性能可移植性,基本勾勒出了这个领域的全貌。
我关注这个方向大概有一年多,从最早看到一些用GPT-4直接生成CUDA的尝试(效果说实话很一般),到后来出现专门做Kernel生成的微调模型,再到最近一批把编译反馈、profiling数据、搜索策略整合进Agent loop的工作,整个演进脉络其实非常清晰。这篇综述我打算按自己的理解,把2026年这个时间点上CUDA Kernel Agent的研究格局梳理一遍——包括它到底分几个技术流派、每个流派的核心方法是什么、实测中哪些环节最容易翻车、以及如果你现在想入局,应该从哪个切入点下手。
需要先说明的是,这个领域变化极快,我写下的很多判断可能半年后就需要修正。但底层的技术逻辑——比如为什么编译反馈比单纯的代码生成更重要、为什么搜索空间的设计比模型本身更关键——这些是不太会变的。文章适合两类人看:一类是做HPC/GPU优化的工程师,想了解Agent能帮自己省多少事;另一类是做AI for Code的研究者,想找一个有明确评测标准的落地场景。两类人关注的点不一样,我会尽量都照顾到。
2. CUDA Kernel Agent的技术版图:四条主流路线
在展开细节之前,先给一个全局的分类框架。我观察下来,目前这个领域大致可以分成四条技术路线,它们不是互斥的,很多工作会交叉使用,但每条路线的核心假设和瓶颈是不一样的。
2.1 路线一:纯生成式——LLM直接写Kernel
这是最早也最直观的思路:给模型一个Kernel的功能描述(比如"实现一个fp16的flash attention forward"),让它直接输出CUDA C++代码。早期的工作基本都在这条线上,代表思路包括用大量CUDA代码做继续预训练、用Kernel相关的指令数据做SFT、以及用单元测试做RLHF。
这条路线最大的问题是正确率天花板低。CUDA Kernel不像普通的Python函数,它对内存对齐、线程束分歧、同步语义极其敏感,模型生成的代码经常"看起来对、跑起来错"。我实测过几个开源的CUDA生成模型,在简单element-wise Kernel上能到70%以上的编译通过率,但一旦涉及shared memory的复杂tiling,通过率会掉到30%以下,性能能超过PyTorch baseline的更是凤毛麟角。
不过纯生成式路线并没有死。它的价值在于作为其他路线的初始化器——先让模型生成一个"大致能跑"的版本,再交给后续的优化环节。现在很少有工作把它当作端到端方案了。
2.2 路线二:搜索增强——把Kernel优化变成参数搜索问题
这条路线继承了传统auto-tuning(比如TVM的Ansor、Halide的autoscheduler)的思想,但用LLM来指导搜索空间的设计和剪枝。核心逻辑是:Kernel的性能主要由一组参数决定(tile size、unroll factor、thread block维度、是否用vectorized load等),与其让模型直接写代码,不如让它输出这些参数,然后由一个模板引擎生成实际代码。
这条路线的好处是正确性有保障——因为代码是从经过验证的模板生成的,不会出现语法或语义错误。瓶颈转移到了搜索效率上:参数空间可能大到10^6量级,暴力搜索不现实,需要LLM根据历史profiling数据做智能剪枝。
我比较看好这条路线在工程落地上的前景,因为它可控、可解释、可复现。缺点是它只能优化"模板能覆盖"的Kernel结构,遇到全新的算法模式就无能为力了。
2.3 路线三:闭环迭代——编译反馈驱动的Agent Loop
这是目前最热、也是我认为最有前途的方向。核心思想是让Agent进入一个"生成→编译→运行→profiling→修改"的循环,每一轮迭代都基于真实的编译错误和性能数据来修正。
这个loop的关键组件包括:一个能解析nvcc编译错误的模块(把错误信息转成模型能理解的prompt)、一个能跑benchmark并采集profiling数据的执行环境、以及一个能根据profiling结果定位瓶颈的"诊断器"。三者缺一不可。
我见过的最有效的实现,是把Nsight Compute的关键指标(achieved occupancy、memory throughput、compute throughput、stall reasons)结构化后喂给模型,让模型针对性地改。比如如果stall reason显示是"long scoreboard",那大概率是global memory访问延迟没被隐藏,模型就会去尝试增加并行度或者用prefetch。
2.4 路线四:多Agent协作——分工模拟人类优化团队
最新的一批工作开始引入多Agent架构:一个Agent负责生成初版,一个负责review代码找bug,一个负责分析性能瓶颈,还有一个负责决定下一步优化方向。这种分工模拟了人类优化团队的工作方式。
多Agent的好处是每个Agent的prompt可以更专注,上下文不用塞太多东西。坏处是通信开销大、容易陷入"互相甩锅"的死循环(比如review Agent一直说有问题,生成Agent一直改不对)。目前这类工作的实测效果方差很大,调得好的能超过单Agent,调不好的还不如单Agent。
下面这张表是我对这四条路线的横向对比,基于我读过的论文和复现过的几个开源实现:
| 路线 | 核心方法 | 正确率 | 性能上限 | 工程落地难度 | 代表场景 |
|---|---|---|---|---|---|
| 纯生成式 | LLM直接输出CUDA | 低 | 中 | 低 | 简单element-wise Kernel |
| 搜索增强 | LLM指导参数搜索 | 高 | 中高 | 中 | GEMM、Conv等模板化Kernel |
| 闭环迭代 | 编译+profiling反馈 | 中高 | 高 | 高 | Attention、融合Kernel |
| 多Agent协作 | 分工协作 | 中 | 高 | 很高 | 复杂多阶段Kernel |
需要强调的是,这个表格里的"性能上限"是相对概念。闭环迭代之所以上限高,是因为它能处理模板覆盖不到的结构创新;但它的方差也大,同样的Agent在不同Kernel上的表现可能差好几倍。
3. 闭环Agent的核心机制拆解:反馈信号到底怎么用
既然闭环迭代是当前最主流的方向,我单独拿一章来拆它的内部机制。很多人以为闭环Agent就是"把编译错误丢回给模型让它改",实际上远不止这么简单。一个设计良好的闭环系统,需要处理好几类不同性质的反馈信号,每类的处理方式都不一样。
3.1 编译错误的分类处理:语法错、语义错、资源错
nvcc的报错信息其实可以分成三大类,Agent对它们的处理策略应该完全不同。
第一类是纯语法错误,比如少了个分号、括号不匹配、类型不匹配。这类错误模型基本能自己修,把原始报错贴回去就行。但要注意,CUDA的模板报错信息极其冗长,一个简单的类型错误可能产生几百行报错。我的经验是只截取报错的前20行和最后10行,中间用省略号代替,这样既保留了关键信息,又不会把上下文撑爆。
第二类是语义错误,代码能编译但结果不对。这类错误最麻烦,因为编译器不会告诉你哪里错了。处理方式是让Agent自己生成测试用例,对比CPU参考实现和GPU输出的差异。我见过一个很巧妙的做法:让Agent在Kernel里插入assert,逐元素检查,定位到第一个出错的index,然后反推是哪一步计算逻辑有问题。
第三类是资源错误,比如register溢出、shared memory超限、occupancy过低。这类错误编译能过,但性能极差或者直接launch失败。处理方式是解析ptxas的verbose输出(-Xptxas -v),拿到register和shared memory的使用量,然后让Agent据此调整。
提示:ptxas的verbose输出是闭环Agent最重要的反馈源之一,但很多实现忽略了它。我强烈建议在编译环节固定加上
-Xptxas -v,把register/spill信息结构化后喂给模型。
3.2 Profiling数据的结构化:别把原始指标直接丢给模型
Nsight Compute能采集几百个指标,但你不能把这几百个数字全塞给模型——一是上下文放不下,二是模型会被噪声淹没。关键是把原始指标转化成"瓶颈诊断结论"。
我的做法是定义一个简单的规则引擎,把指标映射到几个高层结论上:
- 如果
sm__throughput.avg.pct_of_peak_sustained_elapsed> 80% 且dram__throughput< 50%,判定为compute bound - 如果
dram__throughput> 80%,判定为memory bound - 如果
smsp__warp_issue_stalled_long_scoreboard占比最高,判定为global memory latency bound - 如果
smsp__warp_issue_stalled_short_scoreboard占比最高,判定为shared memory latency bound - 如果achieved occupancy < 30% 且没有明显的memory瓶颈,判定为occupancy limited
然后把"compute bound"这样的结论,加上几个关键数字(比如实际occupancy、实际带宽利用率),一起喂给模型。模型看到"memory bound, DRAM throughput 85%, L2 hit rate 40%",就知道该去优化memory access pattern了。
这个规则引擎不需要很复杂,我甚至见过用if-else硬编码的版本,效果就很好。关键是要稳定、可解释,而不是让模型自己去猜指标含义。
3.3 迭代终止条件:什么时候该停
闭环Agent最容易失控的地方是迭代不收敛——模型改来改去,性能在某个值附近震荡,就是上不去。必须设计明确的终止条件。
我一般用三个条件组合:一是性能连续N轮(N=3)没有提升就停;二是总迭代次数超过上限(比如15轮)就停;三是检测到"循环修改"——比如模型把A改成B,下一轮又改回A——立即停。
还有一个容易被忽略的点:保留历史最优版本。Agent的迭代过程不是单调的,经常出现"改坏了又改回来"的情况。所以系统必须记录每一轮的代码和性能,最后返回历史最优的那个版本,而不是最后一轮的版本。
3.4 一个真实的迭代轨迹示例
为了让大家有直观感受,我描述一个我复现过的Attention Kernel优化轨迹(具体数字做了模糊处理):
- 第1轮:模型生成初版,用了朴素的global memory访问,性能是baseline的0.6倍
- 第2轮:profiling显示memory bound,模型引入shared memory tiling,性能到0.9倍
- 第3轮:编译报shared memory超限,模型减小tile size,性能到1.1倍
- 第4轮:profiling显示occupancy只有25%,模型减少register使用,性能到1.3倍
- 第5轮:模型尝试vectorized load(float4),性能到1.5倍
- 第6-8轮:模型在unroll factor和tile size之间反复调整,性能在1.5-1.6倍之间震荡
- 第9轮:触发终止条件,返回第5轮之后的某个最优版本
这个轨迹很典型:前几轮提升快,后面进入平台期。平台期的存在说明Agent的优化策略有上限,要突破需要更激进的变换(比如warp-level的primitive、异步拷贝等),而这些往往是当前模型的盲区。
4. 评测基准与真实性能:别被论文里的数字骗了
聊完机制,必须聊聊评测。这个领域目前最大的问题之一就是评测标准不统一,导致论文里的数字很难横向比较。我见过太多"我们的方法比baseline快3倍"的宣称,仔细一看baseline选的是最朴素的实现,而不是PyTorch或cuBLAS。
4.1 现有基准的局限性
目前常用的几个基准包括KernelBench、TritonBench的CUDA子集、以及一些从实际项目里抽出来的Kernel集合。它们各有问题:
KernelBench的题目偏简单,大部分是element-wise或者reduction,真正难的GEMM/Attention占比不高。TritonBench主要面向Triton而不是原生CUDA,迁移过来有偏差。实际项目抽取的Kernel集合则往往不公开,无法复现。
更关键的是baseline的选择。一个公平的评测应该至少包含三个baseline:PyTorch eager、torch.compile、以及手写的cuBLAS/cuDNN调用。很多论文只跟PyTorch eager比,那当然容易"快好几倍"——因为PyTorch eager本身就没怎么优化。
4.2 我建议的评测协议
如果你要自己评测一个CUDA Kernel Agent,我建议用这样的协议:
| 维度 | 具体要求 |
|---|---|
| 正确性 | 与CPU参考实现对比,相对误差 < 1e-3(fp32)/ 1e-2(fp16) |
| 性能baseline | PyTorch eager + torch.compile + 手写最优实现,三者取最快 |
| 测量方式 | CUDA event计时,warmup 25次,测量100次取中位数 |
| 硬件 | 至少覆盖两代架构(如Ampere和Hopper) |
| 报告指标 | 不仅要报speedup,还要报绝对耗时和达到的TFLOPS |
特别强调跨架构这一点。很多Agent在A100上调出来的Kernel,搬到H100上性能直接腰斩,因为它过拟合了特定架构的cache大小和SM数量。真正鲁棒的Agent应该能在不同架构上都拿到不错的性能。
4.3 论文数字的水分在哪里
我总结了几种常见的"数字注水"手法,读论文时要注意甄别:
第一种是只报最好的case。一个Agent在10个Kernel上有3个表现很好,论文就只报这3个。要看它有没有报中位数和最差case。
第二种是baseline没调优。前面说过了,不赘述。
第三种是忽略了编译时间。有些Agent为了找到一个好Kernel,要跑几百次编译和benchmark,耗时几小时。如果只报最终性能不报搜索成本,那是不完整的。实际落地时,搜索成本往往比性能更重要。
第四种是测试集泄漏。如果Agent的训练数据里包含了测试Kernel的相似实现,那性能好是理所当然的。要看论文有没有做严格的train/test split。
注意:读这个领域的论文时,先翻到实验部分看baseline和测试集设置,比看方法部分更重要。方法再漂亮,评测不严谨也是白搭。
5. 实操中绕不开的五个坑
这一章是我自己踩过的坑,也是我认为任何想入局这个方向的人都必须提前知道的。这些坑在论文里基本不会写,但实际做的时候一个都躲不掉。
5.1 坑一:编译环境的不确定性
CUDA编译对环境的敏感度超出想象。同一个Kernel,nvcc版本差一个小版本,生成的SASS可能完全不同,性能差20%很正常。更麻烦的是,很多Agent系统依赖特定的CUDA版本和GPU架构,换个环境就跑不起来。
我的建议是用容器固定整个工具链,包括CUDA版本、nvcc、驱动、甚至GPU型号。所有benchmark都在同一个容器里跑,保证可复现。如果要做跨架构评测,就准备多个容器,而不是在一个环境里换GPU。
5.2 坑二:benchmark的噪声
GPU benchmark的噪声比CPU大得多。同一段代码连续跑,耗时波动5%-10%是常事,因为GPU有boost clock、有thermal throttling、有其他进程干扰。如果Agent基于单次测量做决策,很容易被噪声带偏。
解决办法是多次测量取中位数,并且用CUDA event而不是CPU计时。另外,测量前要充分warmup,让GPU进入稳定状态。我一般warmup 25次,测量100次。如果两次测量的中位数差异超过3%,就重新测。
5.3 坑三:Agent的"幻觉优化"
这是最隐蔽的坑。Agent有时候会声称自己做了某个优化(比如"我增加了shared memory的使用"),但实际生成的代码里根本没体现,或者体现的方式是错的。更糟的是,有时候代码确实变了,性能也确实提升了,但提升的原因和Agent声称的完全无关(可能是碰巧改了某个无关紧要的地方)。
应对方式是不要相信Agent的自我描述,只看代码diff和实测数据。每一轮迭代都要保存代码快照,性能提升必须能对应到具体的代码变化上。如果对不上,就要警惕。
5.4 坑四:搜索空间的维度灾难
如果你走的是搜索增强路线,很快会遇到搜索空间爆炸的问题。假设有8个参数,每个参数5个取值,那就是5^8≈39万种组合。每个组合编译+benchmark要10秒,全部跑完要45天。
必须做分层搜索:先粗粒度搜大参数(tile size、block维度),锁定大致范围后再细粒度搜小参数(unroll factor、prefetch距离)。另外要利用参数间的依赖关系剪枝——比如shared memory用量和tile size是强相关的,不用独立搜索。
5.5 坑五:过拟合到特定Kernel形状
Agent在训练时见过的Kernel形状上表现很好,换个形状就崩。比如在batch=32, seq_len=512的Attention上调得很好,换成batch=1, seq_len=4096就完全不行。
这个坑的根源是Agent学到了"针对特定形状的trick"而不是"通用的优化原则"。缓解办法是在训练和评测时都覆盖多种形状,并且显式地让Agent理解"为什么这个优化在这个形状下有效",而不是死记硬背。
6. 如果你想入局:从哪切入最划算
最后聊聊落地。如果你现在想在这个方向做点东西,不管是发论文还是做产品,我建议根据自己的背景选切入点。
6.1 有HPC背景的:从闭环系统的工程实现切入
如果你本来就懂CUDA优化,最划算的是做闭环系统的工程实现。这个方向的门槛不在算法,而在工程细节——怎么稳定地采集profiling、怎么结构化反馈、怎么管理迭代状态。这些恰恰是纯AI背景的人做不好的。
具体可以从一个垂直场景做起,比如只做Attention Kernel的自动优化,把这一类Kernel做到极致。不要一上来就想着"通用Kernel Agent",那个太难,而且评测都做不清楚。
6.2 有AI背景的:从反馈信号的理解切入
如果你更懂模型,建议从"如何让模型更好地理解编译和profiling反馈"切入。现在大部分工作是把反馈当纯文本处理,但实际上编译错误和profiling数据都有很强的结构,可以用更专门的方式编码。比如把profiling指标做成一个图结构,让GNN来处理;或者把编译错误解析成AST级别的diff。
这个方向目前做的人还不多,而且有明确的研究价值。
6.3 资源有限的:从复现和评测切入
如果你既没有很强的HPC背景,也没有很多算力,那最务实的做法是做复现和评测。这个领域现在最缺的就是严谨的第三方评测。你可以选几个有代表性的开源Agent,在统一的硬件和基准上跑一遍,把真实数字公布出来。这种工作看起来不"高大上",但对社区的价值极大,而且容易做出影响力。
6.4 一个务实的起步路线
不管选哪个方向,我建议的起步路线是:
- 先搭一个最小的闭环:能生成代码、能编译、能跑benchmark、能采集profiling,先跑通一个最简单的Kernel(比如vector add)
- 然后逐步增加Kernel复杂度:reduction → GEMM → Attention
- 每一步都记录Agent的迭代轨迹,分析它在哪一步卡住
- 针对卡住的环节做改进,而不是一上来就换更大的模型
这个路线看起来很笨,但能帮你建立起对系统行为的直觉。我见过太多人一上来就堆模型规模,结果连系统为什么不收敛都搞不清楚。
我个人在这个方向摸索下来最大的体会是:CUDA Kernel Agent的瓶颈从来不在模型能力,而在反馈信号的质量和迭代策略的设计。一个用中等规模模型但反馈设计精良的系统,往往能打败用大模型但反馈粗糙的系统。这个判断如果只让我说一句话,那就是它了。后续这个方向肯定还会有很多新工作出来,但"反馈质量决定上限"这个逻辑,我预计在相当长时间内都不会变。