ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SparseDitto:用LLM Agentic System自动化稀疏GPU Kernel开发

SparseDitto:用LLM Agentic System自动化稀疏GPU Kernel开发 SparseDitto 这个名字很有意思。“Sparse” 指稀疏而 “Ditto” 在英文里有“同上、照搬”的意思。合在一起就像是说把一种稀疏模式上的优化经验复制、迁移到另一种稀疏模式上去。真正做过 GPU Kernel 开发的人看到这里大概会先苦笑一下。因为实际项目中“迁移”从来不是复制粘贴那么简单。换一种稀疏模式访存方式变了索引计算变了线程组织的逻辑也变了甚至原来好用的模板代码会直接变成性能陷阱。为了适配一种新的稀疏模式手写一个定制 Kernel再花几天时间做正确性验证和性能调优是深度学习和计算系统团队里非常常见、又非常消耗精力的事情。所以当我看到 SparseDitto 这个研究方向时我关注的重点不是“LLM 能写 GPU Kernel”这个噱头而是另一个更实际的问题它能不能把“为不同稀疏模式定制 Kernel”这件事从一次性手工劳动变成一种可配置、可验证、可迭代的工作流。这个方向真正有价值的部分不是模型本身而是它试图重构的这套流程。1. 先搞清楚为什么 GPU Kernel 会跟稀疏模式绑得这么深1.1 “稀疏”不是一种情况而是一类情况稀疏Sparsity这个概念初学者很容易理解成“矩阵里有很多 0所以叫稀疏”。真正写 Kernel 的人不会这么想因为“0 的位置”本身就是信息而且是非常关键的信息。同样的一个稀疏矩阵0 元素可能随机分布也可能按块分布可能集中在某些行也可能出现在固定周期位置。不同的分布方式意味着不同的数据表示方式、不同的访存路径、不同的计算策略。你不能用一套代码处理所有情况就是因为“稀疏”不是一个点而是一族问题。举几个常见类型非结构化稀疏0 的位置没有明显规律需要额外的索引数组记录非零元素的位置。常见于剪枝后的神经网络权重。结构化稀疏0 的分布有规律比如按固定 block 分块。这种模式更容易被硬件高效利用。行稀疏 / 列稀疏整行或整列为 0多见于特定算法和压缩场景。2:4 结构化稀疏每四个连续元素中最多有两个非零是厂商硬件优化过的模式之一。这些模式可能看起来只是“0 的位置不同”但对底层 Kernel 来说差异是骨子里的。非结构化稀疏要做 Gather 操作结构化稀疏可以做 Block 运算行稀疏可能需要改变并行划分策略。数据布局一变一切都要跟着变。1.2 为什么不能一个 Kernel 走天下如果稀疏模式再多样我们能不能写一个通用的稀疏 Kernel通过参数配置适配所有情况理论上可行工程上是灾难。一个 Kernel 要适配所有稀疏模式就必须在运行时处理各种分支判断当前数据块的稀疏类型、决定用哪种访存方式、动态计算索引。这些分支会严重干扰 GPU 的编译优化导致寄存器分配和指令流水线都偏离最优状态。更麻烦的是GPU 的性能关键之一是“访存局部性”不同稀疏模式的数据排布完全不同一个通用 Kernel 除非采用最保守的排布否则必然在某些模式上发生严重的缓存未命中。这就是为什么真实项目里大家宁愿为每种模式单独写 Kernel也不愿意用一个“万能版本”。因为通用版本的性能往往弱得不可用。顺便说一句即使不考虑通用 Kernel直接用现成的稀疏矩阵计算库也不能解决所有问题。成熟库通常针对某几种主流模式做了深度优化但你实际遇到的稀疏模式尤其是算法研究者自己定义的那些特殊模式往往不在库的优化范围里。到了这一步就只能自己写了。1.3 传统定制 Kernel 的方式到底哪里痛过去为一种新稀疏模式写 Kernel大致是这样一个流程阅读论文或算法描述理解稀疏模式的定义。设计数据布局和索引方案。写第一版 Kernel。用参考实现做正确性对比。做 Profiling看访存、占用率、指令效率。反复调优可能还会试不同 tile size、不同 block 配置。最后把它接到项目里跑回归测试。这个流程本身并不神秘真正麻烦的是它是纯手工的而且高度依赖经验。一个熟悉 CUDA 和 GPU 体系结构的工程师面对一种新的稀疏模式至少也要投入几天时间。而且要修改模型结构、调整数据排布又可能引入新的问题。Auto-tuning 工具能解决部分参数搜索问题但它仍然需要人先写出一个可运行的 Kernel 版本再去搜参数。模板代码库能节省一部分重复工作量但稀疏模式一变模板往往要改得面目全非。所以这个领域真正的痛点不是“没有工具写 Kernel”而是“适配一种新稀疏模式的成本太高”。SparseDitto 这类 LLM-based agentic system 的目标正是希望降低这个适配成本。2. SparseDitto 这类系统真正在做什么2.1 用“描述”代替“手写”从标题字面来看SparseDitto 的输入是稀疏模式的描述输出是定制的 GPU Kernel 代码。这个交互方式本身已经和传统开发不同。传统方式里工程师要自己完成从“模式”到“代码”的完整映射。而现在这类系统试图让使用者以更高层的语言描述需求由 LLM-based agent 去补全中间的细节。在一个更完整的工程化设计里这个流程可能是这个样子的稀疏模式描述稀疏类型、数据排布、预期形状 ↓ LLM Agentic System分析 → 生成 Kernel → 编译校验 → 错误反馈 → 迭代修改 ↓ 输出候选 GPU Kernel ↓ 人工/自动化验证正确性、性能对比 ↓ 合入项目这个流程的价值不是每次都能一次生成完美代码。而是它把“写第一版 Kernel”这件事自动化了。人工介入从“从零编写”变成了“描述需求 验证结果 不断纠偏”这已经是一个工作流级别的大变化。2.2 Agentic System 和“一句话生成代码”不是一回事很多这类工具介绍提到 LLM用户的第一反应是“AI 写代码”。但把 SparseDitto 叫做 Agentic System说明它不是一个简单的一次性生成模型。核心区别在于一个可以迭代的系统会比我“写一段话、拿一段代码”要可靠得多。一个 agentic system 通常会完成这样的循环理解输入解析稀疏模式描述判断是哪种类型的稀疏。检索知识查找已有的 kernel 模式、库函数或优化技巧。生成候选代码写出一版 Kernel。自动检查尝试编译如果编译失败读取报错信息定位问题。迭代修复根据错误信息调整代码直到通过编译。反馈完善如果可能加入测试结果和性能信号进一步优化。这种“生成 → 编译 → 读取报错 → 修改”的闭环非常贴近有经验的工程师自己写代码的方式。它不是靠一次大模型的“灵感”产出可用代码而是把“不断试错”这个过程本身也自动化一部分。所以 SparseDitto 这类系统的价值不只是 LLM 比以前更强的代码生成能力而是 agent 机制让这种生成行为变得可控、可迭代、可验证。后一点才是它有可能承接真实工程任务的原因。2.3 从“写一个 Kernel”到“配置一个 Kernel 生成流程”我在前面的段落里反复强调流程是因为对工程系统来说长期价值不在单个输出而在流程本身。假设你接到了一个任务为一个新的稀疏模式写一个 Kernel。传统方式下你会进入一次临时的、高度专注的、充满隐式知识的手工过程。完成后代码留在项目里但过程中积累的经验大多存留在你个人的记忆里。而 SparseDitto 这类系统出现后任务变成把稀疏模式描述清楚提供一个或几个用于验证的输入样例定义正确性标准比如和密集参考实现比较的误差范围运行 agent 系统得到候选 Kernel审核代码跑性能对比合入项目。同一个流程以后遇到另一种稀疏模式可以再走一遍。前面定义好的验证样例、错误反馈规则、性能基线都可以复用。这才是真正的改变把 kernel 适配工作从“一次性手工艺”变成“可配置的半自动流水线”。2.4 和传统模板框架、自动调优工具的差异既然话题来到了工具对比就多说几句边界。模板框架比如许多知名计算模板库解决的问题是把一些常见的 kernel 结构抽象出来用户通过参数修改 tile 大小、数据类型等。它假设你已经知道自己的场景大致属于哪类模板。稀疏模式边界不清晰、或者超出模板假设时模板反而变成束缚。自动调优工具解决的问题是在一组可调参数空间里找到性能最好的配置。但前提是已经有一个可编译、可执行的 kernel 版本。SparseDitto 这类 agentic system 处在更上游的位置它不只是调参而是生成代码本身。也就是说它跟模板框架和自动调优工具不是互斥关系更合理的定位是互补。生成之后再交给调优工具去搜参数是完全合理的工程链路。注意这里有个很关键的点如果你遇到的稀疏模式是成熟的库函数已经优化好的比如主流库已覆盖的模式就不需要这类系统出马。这类系统最适合的是长尾的、没有被封装成库的、面向特定研究或特定业务的稀疏模式。3. 把这类方案放进真实工作流要过哪几关3.1 第一关正确性验证LLM 生成的代码有一种迷惑性——它看起来非常自然结构完整注释清晰似乎能跑。但稀疏 Kernel 恰恰是“看起来对实际错”的高发区。稀疏 Kernel 的逻辑复杂度集中在索引计算。一个 offset 算错一个边界没判断一个 mask 没做结果就可能出现部分元素被错误覆盖。更麻烦的是有些错误只在特定形状、特定非零元素分布下才会触发小样本测试根本测不出来。正确性验证的基本盘必须包含一个参考实现可以是密集版本的逐元素计算也可以是手写的简单稀疏实现。多种形状、多种稀疏率的输入样例。非零元素位置随机分布和结构化分布两套样例。结果逐元素对比设置可接受的误差阈值。在真实项目里我会建议把验证样例做成自动化测试的一部分而不是跑一次就不管了。因为后续修改 kernel 生成逻辑时所有历史样例都可以做回归验证。3.2 第二关性能验证一个 Kernel 正确不等于有用。对没做过 GPU 性能调优的人来说最容易出现的情况是LLM 生成的代码正确运行性能却不比普通 CPU 版本快多少甚至更慢。这里有一个底层原因需要理解GPU 性能不是看“代码能不能跑”而是看“指令流水线满不满、访存效率高不高、寄存器压力大不大”。LLM 生成代码时它能学到一些显式模式但很难真正验证它在当前硬件上的实际效率。所以性能验证必须依赖测量而不能依赖直觉。具体的步骤是先跑一个小输入确认输出正确。再跑一个接近真实规模的输入测量 kernel 耗时。和基线对比基线可以是手写 Kernel、现有库的实现或者 dense 实现。用 Profiling 工具看访存吞吐、计算吞吐、活跃线程数等指标找到瓶颈。如果性能明显落后把它交给 Profiling 工具或 Auto-tuner 去调整参数往往比反复改生成提示词更有效。注意不要对 LLM 生成的第一个 Kernel 版本期待过高。一个能通过正确性验证、比 naive 版本快、且具备可读性的 Kernel已经是很好的起点。3.3 第三关回归测试与代码归档很多团队在使用 AI 生成代码时会忽略一个工程化问题代码入库之后怎么保证后续不被意外破坏。代码一旦进入项目它就不再只是“生成出来的东西”而是需要长期维护的资产。因此需要把生成它的稀疏模式描述、验证样例、生成日志一起归档在 CI 里添加 Kernel 相关的正确性测试记录生成时代的环境信息模型版本、工具链版本方便以后复现问题如果更换了 LLM 版本或生成的 Agent 配置用历史样例重新跑一遍回归测试。这看起来像是额外工作但非常重要。否则就会出现一种尴尬情况一个 Kernel 修了一个 bug过了两个月发现是新模型生成逻辑变了导致回归而你的测试集里根本没有相关的用例。3.4 第四关安全和效果边界LLM 生成 Kernel 还有一个容易被低估的问题自动生成的代码可能违反某些隐含约束比如显存越界访问、没有同步、假设了错误的 grid 维度等。这类问题比普通逻辑 bug 更危险因为它可能不会报出明显错误而是让数据在内存里被悄悄写坏最终表现为一个非常难排查的诡异结果。所以任何自动生成的 GPU 代码在接入真实数据前建议至少做这些安全检查用 Compute Sanitizer 或等价工具检查越界访问和未初始化内存检查是否所有线程路径都有明确的 return检查共享内存大小是否随输入动态变化是否可能超出限制在真实数据之外用边界形状做一次压力测试例如超大 batch、接近 0 的稀疏率、极端宽高。我甚至会更进一步建议在项目初期固定使用一个经过验证的“审查路径”不管是谁生成的 Kernel接入主线前都得过一遍编译、正确性、性能、内存安全四关。这四关不需要人工逐行审代码但它能兜住绝大多数灾难性问题。3.5 排查链路生成后的 Kernel 跑不通按什么顺序查不管工具多强总会遇到跑不通的时候。这里的排查顺序非常重要我建议按这个链路走先看错误类型是编译报错、运行崩溃、输出错误还是性能异常不同错误对应不同层级。再查输入描述与数据布局稀疏模式描述是否和实际数据一致。比如你说它是行稀疏但数据实际是列稀疏存储那生成的索引逻辑必然出错。检查编译环境与依赖版本CUDA 版本、编译器版本、GPU 架构arch是否符合生成代码的假设。检查生成代码的索引和边界重点看 offset 计算、mask 判断、block 边界处理。检查运行时的资源约束显存、共享内存、线程数限制。最后检查性能表现如果逻辑全部正确但性能不理想再进入 Profiling 阶段。这个链路不一定能解决所有问题但能避免一个更常见的错误代码跑不通时直接反复修改提示词寄希望于“再生成一次就对了”。4. 怎么判断一个 Kernel 能不能用五个维度我建议用一个五维框架来评估 LLM 生成的 Kernel每个维度都要有明确的验证方法而不是凭感觉。维度核心问题验证方法不通过时的表现正确性生成的 Kernel 是否在所有测试输入下得到期望结果和参考实现逐元素对比覆盖多种 shape 与稀疏率输出不一致、随机偶发错误性能是否达到可接受的加速比和基线 Kernel、库实现对比耗时比 baseline 还慢、占用率离谱资源与内存安全是否存在越界、未初始化访问或资源超限Sanitizer 检查、压力测试运行时崩溃、数据被污染可读与可维护工程师能否在 1-2 小时内理解其核心逻辑人工代码审查无法解释关键索引计算、风格混乱泛化与回归调整模型结构或输入 shape 后是否仍能正常工作修改 shape 复跑回归测试只对固定 shape 有效、输入一变就错这个框架的价值是把“这个 Kernel 能用来交差吗”这种模糊问题拆成五个可执行的具体检查项。任何一项不过关就不应该进入下一步。对你具体落地来说我会强烈建议把前两项放在最前面。一个 kernel 如果性能再好但正确性本身有问题那它只是给后续排查问题埋雷。一个 kernel 如果正确但性能完全没竞争力则没有实际价值。实际操作里还有一个经验不要只测“成功路径”。很多 Kernel 会在小 case 上表现完美但遇到 batch size 不是某个对齐数、一些非零元素恰好落在 block 边界等场景就可能出错。因此测试矩阵里至少包含最小可用输入接近真实规模的输入极端稀疏率的输入非规则形状的输入。5. 这类系统适合谁不适合谁5.1 适合谁首先是算法和模型研究者。他们的工作经常产生自定义的稀疏模式但这些模式往往不会得到主流库的一等优化支持。过去为了让一个研究原型跑得快一点他们甚至需要自己写 CUDA学习成本很高。SparseDitto 这类系统如果能把“给模型结构匹配一个可用 Kernel”的门槛降低会让这类研究者把精力放回模型本身。其次是内核工程师和性能工程师。他们不需要系统替代自己写 Kernel而是希望系统帮他们完成第一版草稿或者在面对一批长尾稀疏模式时快速生成候选实现再靠自己的经验做调优。对他们来说这像是多了一个“能同时搜索大量模板的实习生”。第三是学习 GPU 编程的人。通过观察 LLM 如何为不同稀疏模式生成 Kernel再结合编译结果和 Profiling 数据可以学习到很多模式化的写法。但前提是你必须能读得懂生成代码不然这个东西只会让你产生一种“我可控”的错觉。5.2 不适合谁如果你对 Kernel 完全不懂也不打算懂只是想把任务丢给模型自动生成然后直接用于生产环境那我是明确不推荐的。理由非常简单LLM 生成的代码同样需要人工验证和审核而“不懂 Kernel”的人恰恰没有判断能力。你可能得到一个看起来正常、实际在某种输入下会静默写坏内存的 Kernel然后等到线上环境出现诡异 bug 时才知道这个坑埋在哪里。如果某个稀疏模式已经有一个成熟库优化得很好你也不需要这类系统。比如标准矩阵乘法中一些硬件强支持的结构化稀疏模式直接用官方库的性能通常会超过临时生成一个 Kernel 再调优所花费的时间和精力。如果你的项目对正确性要求极高同时没有完整的验证链路和回归测试体系也要谨慎。这跟 AI 生成代码没有直接关系而是任何新写的 Kernel 都不应该直接空降到这种系统里除非你已经搭好安全网。5.3 使用这个方向所需的前置条件能读懂 CUDA 或等价 GPU Kernel 代码至少知道索引、block、thread 这些概念有真实输入数据和参考实现否则无法做正确性验证有可量化的性能基线理解 Profiling 工具怎么读愿意为验证和回归测试预留时间。如果一个团队五个条件都满足我认为他们引入这类方案的风险是可控的。缺少其中任何一条都应该先把缺口补上。6. 从尝鲜到工程化一个可复用的落地路径6.1 第一步先用一个已有方案的模式做“反向验证”不要一上来就拿全新的稀疏模式做测试。先找一个已经有手写 Kernel 实现的模式用 SparseDitto 这类系统重新生成一版 Kernel然后和现有实现做对比。这一步首先要验证的是正确性其次才是性能。因为是反向验证你手上有正确的参考答案非常容易定位错误。如果这一关过不了说明系统的输入描述方式或验证流程还有问题要先修正。6.2 第二步建立验证基础设施真正的落地前提不是模型效果而是测试基础设施。需要做到把参考实现写成可复用的函数把测试样例组织成多维参数组合shape、稀疏率、数据布局在 CI 里增加一个“生成 Kernel 跑测试”的任务流程保存生成的 Kernel、描述、日志和当时的环境信息。这套基础设施的意义是以后每次换提示词、换模型版本、换 agent 配置都可以基于历史样例做回归验证。没有这套东西生成结果的稳定性就无从谈起。6.3 第三步选择低风险场景试点在完整工程化之前选一个低风险的内部场景做试点数据不是核心生产链路出错也不会造成重大影响。比如一个离线分析实验的矩阵算子。在试点过程中重点观察以下信号从描述到可用 Kernel 的迭代次数正确性测试的通过率和一次通过率生成代码的性能与手写实现的比例差距人工审核代码大概需要多长时间出问题时能否通过 agent 的错误反馈自己修复到可用状态。这些信号直接决定这个方向能否进入更高风险的场景。6.4 第四步再扩展到新稀疏模式当验证基础设施和试点都稳定后再拿它去啃那些真正麻烦的长尾稀疏模式。这时你已经有了基准流程也知道哪些地方容易出错成本会远低于第一次探索。在这个阶段我还建议把经验沉淀下来什么样的稀疏模式描述更容易被 agent 理解什么样的验证样例能最快暴露错误哪些错误需要人工介入而不是继续让 agent 重试这些问题答案才是团队真正的工程资产。6.5 长期维护把提示词和验证集当成产品资产最后提醒一件事LLM 版本更新、agent 策略调整、底层工具链变化都会影响生成代码的质量。建议把以下视为需要长期维护的产品资产稀疏模式描述模板描述得越规整生成质量越稳定验证样例与测试集每次生成回归测试的依据基线性能记录用于判断新生成版本是否值得替换旧版本错误案例库记录哪些情况下 agent 反复失败为什么会失败。这就像积累一套“编译器测试用例”一样它不会直接产生收益但能防止这个方向在实际项目中越来越不可控。收尾说回到 SparseDitto 这个名字Ditto 是“同上、照搬”但做 GPU Kernel 的人都知道稀疏模式之间从来不存在真正“照搬”就能用的情况。SparseDitto 这个方向的有趣之处恰恰是用 agentic system 把“照搬”变成了“理解差异后自动调整”。它真正改变的不是你会不会写 CUDA而是“为一个新的稀疏模式定制 Kernel”这项任务的成本结构从以天为单位的人工开发变成“描述模式 自动化迭代 人工审核验证”的半自动流程。这个转变对研究原型验证、长尾稀疏模式适配和团队知识资产沉淀都是有实际意义的。但我也要反复强调一句听上去不够“AI 时代”的话这类系统的输出仍然只是候选实现。真正决定一个 Kernel 能不能进入生产环境的标准还是那几条——正确性、性能、内存安全、可维护性、可回归性。LLM 负责把第一版代码写出来而判断和使用它的能力依然在工程师自己身上。先搭好验证链路再让这条链路带着模型跑起来这比任何模型升级都重要。
返回列表