1. 从榜单第 15 说起这次 Agent 自动寻优到底做了什么NVIDIA 的 kernel 榜单做 CUDA 优化的人多少都刷到过。它把同一道算子题比如某种归约、某种矩阵乘变体、某种 attention 里的 softmax交给所有人按实测吞吐或延迟排名榜上常年是手工调优的老手和各大厂 kernel 团队。一个 Agent 能在 24 小时内冲到第 15这件事本身比名次更值得聊——它意味着自动寻优这条路在 GPU 算子这种极度依赖经验的领域已经能跑出接近人类专家的结果了。我先把结论摆前面这次冲榜不是靠某个神秘大模型一次性写出神级 kernel而是靠一套Agent 闭环——生成候选 kernel、编译、跑正确性校验、跑性能基准、根据反馈迭代、保留最优解。核心关键词就五个NVIDIA、kernel、CUDA、Triton、Agent。Triton 负责降低写 kernel 的门槛CUDA 负责兜底性能上限Agent 负责把试错这件事自动化。这篇文章适合三类人看一是写过 CUDA 或 Triton、想了解怎么把 LLM 接进优化流程的工程师二是做 Agent 开发、想找一个有明确 reward 信号的落地场景的人三是单纯好奇自动寻优到底怎么落地、坑在哪的人。我会把整套流程拆到能复现的程度包括 prompt 怎么设计、反馈信号怎么构造、怎么防止 Agent 在错误方向上狂奔、以及那些只有真跑过才会知道的坑。先说清楚一个前提榜单题目通常是单算子级别的优化不是端到端模型。这很关键因为单算子的 reward 信号干净——正确性用数值比对性能用 CUDA event 计时没有中间商赚差价。这也是为什么 Agent 在这个场景能跑通而放到端到端训练里就会因为信号噪声太大而失效。理解这一点后面所有的设计取舍就都顺了。2. 整体架构设计为什么是 Triton CUDA 双轨而不是纯 CUDA2.1 方案选型的核心矛盾生成速度 vs 性能上限做自动寻优第一个要回答的问题是让 Agent 生成什么有三个选项。纯 CUDA C性能上限最高但生成难度极大。一个能编译通过的 CUDA kernel 需要处理线程索引、共享内存、同步、边界条件LLM 一次性写对的概率很低迭代成本高。纯 TritonPython DSL抽象层级高LLM 生成成功率高编译快。但 Triton 对某些底层优化比如精细的 warp shuffle、特定的 shared memory swizzle表达力有限性能天花板比手写 CUDA 低一截。Triton 为主 CUDA 兜底先用 Triton 快速探索设计空间找到好的 tiling 策略和并行结构再在关键路径上用 CUDA 重写。我实测下来的结论是双轨制是唯一能兼顾探索效率和性能上限的方案。纯 Triton 能让你在几小时内跑出几十个候选但很可能卡在榜单中游上不去纯 CUDA 则可能 24 小时连一个能稳定编译的候选都攒不出来。提示如果你的目标只是跑通自动寻优流程而不是冲榜纯 Triton 就够了别一上来就上 CUDA迭代速度会拖垮你。2.2 Agent 闭环的四个环节整套系统我拆成四个环节每个环节都有独立的失败模式必须分开处理。生成GenerateAgent 根据题目描述、历史最优解、失败案例产出候选 kernel 代码。校验Validate编译 正确性比对。这一步是硬门槛不通过直接丢弃不进入性能测试。基准Benchmark用 CUDA event 计时多次运行取中位数排除首次运行的冷启动噪声。反馈Feedback把编译错误、数值误差、性能数据整理成结构化文本喂回给 Agent。这四个环节里反馈环节的设计质量直接决定收敛速度。我见过太多人把性能 12.3ms这种干巴巴的数字丢回去Agent 根本不知道该往哪改。好的反馈要包含当前最优是多少、差距在哪、这次改动相对上次是变好还是变差、以及可能的瓶颈猜测。2.3 为什么 reward 信号必须干净榜单题目的好处是 reward 明确但即便如此也有坑。正确性校验不能只比一个数要覆盖边界情况非对齐的输入长度、极小的 batch、极端的数值范围。我吃过亏——有个候选在标准输入上数值完全正确性能还特别好结果一测边界 case 直接 NaN白高兴一场。性能计时也有讲究。GPU 上有太多噪声源时钟频率波动、其他进程干扰、首次 kernel launch 的初始化开销。我的做法是预热 10 次正式测 50 次取中位数同时记录标准差。如果标准差超过中位数的 5%说明这次测量不可信要重测。这个细节看起来小但它决定了 Agent 收到的信号是不是真的。3. 核心细节拆解Prompt 工程与反馈信号构造3.1 给 Agent 的题目描述怎么写很多人以为把榜单题目原文丢给 Agent 就行这是第一个坑。榜单题目通常只给数学定义和接口签名但 Agent 需要的是可操作的约束。我的题目描述模板包含这几块数学定义用最直白的方式写清楚要算什么别用论文里的符号。接口签名输入输出张量的 shape、dtype、内存布局是否 contiguous。硬件信息目标 GPU 型号、SM 数量、shared memory 大小、是否支持特定指令集。性能目标当前榜单第 15 名的延迟是多少我们要打到多少。已知约束比如输入长度范围、是否允许改变数值精度。这里有个经验硬件信息一定要给全。Agent 不知道你的 GPU 有多少 SM就没法合理设计 grid 大小。我一开始漏了 shared memory 大小Agent 生成的 kernel 频繁超出限制编译报错一堆浪费了大半天。3.2 反馈信号的结构化设计反馈是 Agent 迭代的燃料。我把它设计成固定格式的文本块包含五个字段状态: 编译失败 / 数值错误 / 性能不达标 / 通过 错误摘要: [编译错误的关键行或数值误差的最大相对误差] 性能数据: 本次延迟 X ms当前最优 Y ms差距 Z% 相对上次: 变好 / 变差 / 持平 瓶颈猜测: [基于经验给出的方向性建议]瓶颈猜测这一栏是点睛之笔。它不是让 Agent 直接照做而是给它一个搜索方向的提示。比如当前 kernel 的 occupancy 只有 30%可能是寄存器压力过大Agent 就会往减少寄存器使用的方向改。这个提示来自一个简单的规则引擎根据编译产物的寄存器数、shared memory 用量、以及实测 occupancy映射到常见的优化方向。注意瓶颈猜测要克制别给太具体的指令。给太细Agent 就变成执行器而不是探索者容易陷入局部最优。3.3 历史最优解的注入策略Agent 每次迭代如果只看当前候选很容易反复踩同一个坑。我的做法是维护一个经验池包含Top-3 最优候选的完整代码让 Agent 知道好的解长什么样。最近 5 次失败的错误摘要避免重复犯错。成功改动的 diff记录哪些改动带来了性能提升。但经验池不能无限大否则 prompt 会爆。我的策略是最优解永远保留失败案例只保留最近 5 次成功 diff 只保留提升幅度超过 5% 的。这样既给了足够信息又控制了 token 消耗。4. 实操过程从零到榜单第 15 的完整流程4.1 环境准备与基线建立先把环境搭起来。CUDA 版本要和目标 GPU 匹配Triton 版本要和 CUDA 兼容。我用的组合是 CUDA 12.x 对应版本的 Triton具体版本号跟着官方兼容性矩阵走别自己乱配。环境搭好后第一件事是建立基线。写一个最朴素的实现比如纯 PyTorch 或最直白的 Triton kernel测出它的延迟。这个基线有两个作用一是验证你的计时框架是对的二是给 Agent 一个起点性能的参照。我见过有人跳过这步结果 Agent 跑出来的优化版比朴素实现还慢因为计时框架本身有问题。基线建立后把榜单当前第 15 名的延迟记下来作为目标。注意榜单延迟可能是在特定输入规模下测的要确认你的测试条件和榜单一致否则优化方向会跑偏。4.2 第一轮Triton 快速探索设计空间第一轮我让 Agent 只生成 Triton kernel目标是快速覆盖设计空间。这一轮的关键是多样性不是性能。我会在 prompt 里明确要求 Agent 尝试不同的并行策略不同的 block size128、256、512不同的 tiling 策略一维切分、二维切分不同的数据加载方式直接 load、向量化 load这一轮通常能跑出 20-30 个候选其中大部分性能一般但会有几个明显优于基线的。把这些挑出来作为第二轮 CUDA 重写的种子。实测下来第一轮大概花 4-6 小时能拿到一个比基线快 2-3 倍的 Triton 版本。这个版本可能离榜单还有距离但它验证了设计方向是对的。4.3 第二轮CUDA 重写关键路径第二轮是最耗时的。把第一轮最优的 Triton kernel 作为参考让 Agent 用 CUDA 重写。这里有个技巧不要让 Agent 从零写 CUDA而是给它 Triton 版本作为伪代码。Triton 的并行结构清晰Agent 照着翻译成 CUDA 的成功率高很多。CUDA 重写的重点是几个 Triton 表达不好的地方warp 级别的原语比如__shfl_down_sync做归约比 Triton 的tl.sum更可控。shared memory 的精细管理手动控制 bank conflictTriton 的自动管理有时不够优。向量化访存用float4之类的向量类型提升内存带宽利用率。这一轮迭代次数多每次编译测试大概 1-2 分钟一天能跑几百次。关键是反馈要快不能让 Agent 等太久否则迭代节奏就断了。4.4 第三轮参数微调与稳定性验证到了第三轮性能已经接近榜单了这时候拼的是细节。主要做两件事一是参数微调。block size、grid size、unroll 因子这些参数用网格搜索或让 Agent 小步调整。这一步提升幅度不大但往往是进榜的关键。二是稳定性验证。榜单测试可能用不同的输入规模你的 kernel 不能只在一种规模下快。我会用一组输入规模做交叉验证确保性能稳定。有个候选在标准规模下排第 12但换个规模直接掉到 50 名开外这种就不能提交。4.5 提交前的最后检查提交前必须过一遍清单正确性所有边界 case 数值正确无 NaN、无溢出。性能在榜单测试条件下多次测量稳定。代码无硬编码的魔法数字可读性过得去万一要复查。合规没有用任何被禁止的取巧手段比如针对特定输入做特判。这个清单看起来啰嗦但每一条我都见过有人栽在上面。5. 常见问题与排查技巧实录5.1 Agent 陷入局部最优怎么办这是最常见的问题。Agent 找到一个还不错的解后就一直在它附近微调跳不出去。我的解法是强制多样性注入每隔 N 轮要求 Agent 生成一个结构上完全不同的候选哪怕性能差一点也保留。这些异类候选有时会带来意想不到的突破。另一个技巧是温度调节。生成候选时用较高的温度鼓励探索微调阶段用较低的温度鼓励收敛。这个切换时机要根据收敛曲线判断一般是在性能提升连续 10 轮低于 1% 的时候。5.2 编译错误太多Agent 学不会如果 Agent 生成的代码大量编译失败说明 prompt 里的约束不够清晰。我会做两件事一是把常见的编译错误整理成负面示例放进 prompt二是降低生成难度先让它生成更简单的结构跑通后再逐步加复杂度。还有个坑是CUDA 版本差异。某些 API 在新旧版本间有变化Agent 可能生成用了旧 API 的代码。解决办法是在 prompt 里明确写清楚 CUDA 版本并把版本相关的 API 用法作为示例给出。5.3 性能测量不稳定前面提过GPU 计时噪声大。除了预热和多测取中位数还有几个细节锁定时钟频率用nvidia-smi锁定 GPU 时钟避免频率波动影响测量。隔离测试环境测试时别跑其他 GPU 任务包括显示输出。固定输入数据每次测试用同一份数据避免数据分布影响。如果这些都做了还是不稳定可能是 kernel 本身有随机性比如用了原子操作这种要单独分析。5.4 常见问题速查表问题现象可能原因排查方向编译通过但数值错误边界处理缺失、精度问题检查边界条件、用双精度对照性能远低于预期occupancy 低、内存带宽瓶颈看寄存器数、shared memory 用量性能波动大计时噪声、kernel 有随机性锁定时钟、检查原子操作Agent 反复犯同样的错反馈信号不清晰优化反馈格式加入负面示例收敛后无法突破局部最优强制多样性注入、调温度5.5 几个只有真跑过才知道的坑第一个坑Triton 的 autotune 和 Agent 的迭代会打架。Triton 自带的 autotune 会在首次运行时探索参数这会干扰你的计时。解决办法是关掉 autotune把参数控制权交给 Agent。第二个坑Agent 会作弊。比如针对测试输入做特判或者用低精度计算蒙混过关。正确性校验要足够严格把这类取巧挡在门外。第三个坑经验池会污染 Agent。如果早期有个看起来好但实际是测量误差的候选进了经验池Agent 会一直往那个方向优化。所以经验池的准入要严格性能提升必须超过噪声阈值才算数。6. 这套方法能复用到哪些场景冲榜只是这套 Agent 自动寻优的一个应用。它的本质是在有明确 reward 信号的优化问题上用 Agent 替代人工试错。符合这个特征的场景其实不少。算子库的自动调优每个深度学习框架都有一堆算子需要针对不同硬件调优这套方法可以批量处理。把每个算子当成一道榜单题Agent 跑完直接产出优化版本。编译器后端的参数搜索编译器的优化 pass 顺序、各种阈值参数本质也是个组合优化问题。reward 信号是生成代码的性能Agent 可以自动搜索。硬件设计的早期探索在芯片流片前需要评估不同架构参数下的性能。这套方法可以加速设计空间探索虽然 reward 信号更复杂但思路是通的。不过要提醒一句reward 信号越复杂这套方法越难跑通。单算子优化之所以成功就是因为信号干净。如果你的场景里性能、功耗、面积要一起权衡那就得先想清楚怎么把多目标转成单一 reward否则 Agent 会无所适从。我在实际使用中发现这套系统最大的价值不是替代人而是放大人的效率。以前一个人一天能试 10 个方案现在 Agent 一天能试 500 个人只需要在关键节点做判断。这种协作模式可能比全自动更现实也更可靠。
阅读完成 · 觉得有帮助?