
看到MLSys 2026上PIKE这个标题第一反应是“多智能体 推理加速 2.88倍”这几个词组合在一起确实有点反差感。PyTorch推理优化在业内早不是新话题torch.compile、TensorRT、CUDAGraphs哪一个拿出来都能讲一堆但“多智能体”这个定语在这里不是加个噱头而是代表了一套新的优化思路不再由人手工指定融合规则、也不靠单一编译器做端到端变换而是把“分析计算图、找瓶颈、生成算子、验证收益”这些环节拆给不同角色的智能体分工协作让优化过程本身具备自适应和可扩展性。这篇文章我不想复述论文摘要而是把PIKE这类系统拆开揉碎先看Eager模式推理到底慢在哪再看多智能体为什么适合干这件事然后分析2.88倍这个数字背后都来自哪些优化空间最后落到实际操作——即使你暂时用不上完整的智能体框架也能从这套思路里提炼出一份能直接用在自研推理服务上的优化优先级清单。顺便把我自己在做推理加速benchmark时踩过的坑也一起交代清楚。1. Eager模式为什么慢一次推理调用里的大部分时间都去哪了要理解PIKE这类系统的价值先得理解它对比的基线——PyTorch Eager模式——为什么慢。很多人一听到“PyTorch推理慢”就直觉归因于“Python循环慢”“GIL锁”“算子没有融合”这个方向没错但不够精确。你只有把Eager模式下时间消耗的构成拆到微秒级才能明白2.88倍到底从哪来。1.1 每一次算子的“完整旅程”远比你想象的长在Eager模式下你调用self.conv(x)时发生的完整链路大致是Python层的nn.Module.__call__进入forward这里已经有Python函数调用开销进入底层C dispatcher根据tensor的device、dtype、layout等属性做dispatch逻辑判断根据dispatch key解析到对应的kernel实现完成必要的shape、stride推导检查尺寸合法性kernel实际执行。如果是GPU算子这一步内部还包含CPU端launch操作由CPU向GPU提交kernel launch命令返回结果如果产生了中间tensor且没有显存复用还会涉及缓存的分配/释放。一个conv算子的GPU kernel实际执行时间在几微秒到几十微秒之间取决于输入尺寸但CPU端的launch开销、Python层调用开销、dispatch开销加起来往往和kernel执行时间处于同一数量级甚至更高。这就导致一个后果模型层数越深、算子越小、batch越小计算时间占比越低Eager模式引入的固定开销占比越高。很多小模型在GPU上推理时实际GPU利用率不到30%剩下的时间都在等待CPU指令和kernel提交。1.2 内存分配是被严重低估的隐形杀手另一个容易被忽略的点是中间tensor的分配。Eager模式中每个算子都产生新的输出tensor如果把PyTorch的caching allocator比作一个缓存仓库它的“命中”并不总是完美。动态shape、不同分支、padding变化都会导致缓存块不匹配产生新的cudaMalloc调用。一个典型的ResNet推理中可能有几百次tensor分配cudaMalloc的代价比大多数人想的高一个量级。这就解释了两个常见现象一是为什么把torch.no_grad()加上、又开了torch.inference_mode()提升仍然有限——你只是少了一部分自动求导相关的bookkeeping但dispatch和alloc开销还在二是为什么在显存足够的情况下反复推理时间也不稳定——分配器缓存状态不同。1.3 编译器模式解决了一部分但没有解决全部torch.compile的出现就是为了解决Eager模式的这些固定开销通过图捕获、算子融合、Triton kernel生成等方式减少launch次数、消除中间tensor。它的效果在小模型和小batch上非常显著实测确实能达到1.5到3倍。但torch.compile也有它的边界编译时间不可控、某些动态控制流不兼容、自定义算子支持有限而且它本质上是“人来定义优化规则、编译器去匹配执行”。当规则覆盖不到你的网络结构时收益就上不去。PIKE这类多智能体系统的核心差异就在这里它不再是“规则匹配 模板生成”的固定管线而是把整个优化过程变成多角色分工协作的“团队任务”每个角色负责一块优化空间互相提供信息、验证结果形成一个持续搜索和迭代的闭环。这是我在看到这个标题时觉得最值得展开的部分。2. 多智能体优化的逻辑PIKE把优化问题拆成了什么多智能体在推理加速上的用法不是一个“大模型agent”在帮你写代码而是典型的“master-worker”协作范式。每个智能体有自己的观察空间、动作空间和评价指标多个智能体围绕同一个计算图协同工作最终产出一个优化后的执行计划。2.1 五类角色划分与各自的职责边界从我看到的系统设计思路来推断PIKE这类框架大概率会把优化流程拆成下面五类角色分别对应推理优化链条的不同环节智能体角色核心职责输入信息输出产物Profiler/分析智能体找出瓶颈算子、定位时间热点计算图、profiling数据、硬件信息瓶颈清单、热点排名Pattern/融合智能体发现可融合的计算子图模式计算图结构、算子语义融合候选子图CodeGen/算子生成智能体为特定子图生成高性能kernel融合后子图、硬件特性、Triton/CUDA模板新kernel代码Scheduler/调度智能体处理执行顺序、stream并行、内存复用、CUDA Graph捕获依赖关系、kernel耗时估计、显存约束执行计划Verifier/验证智能体校验正确性与收益原始输出、新kernel输出、benchmark结果验证报告、是否采纳的决策这五个角色不是流水线跑一遍就结束而是多轮迭代。分析智能体发现问题后融合智能体提出候选CodeGen生成kernelVerifier给出“这次改动快了多少、结果是否符合精度要求”的结论然后分析智能体基于新状态继续找下一个瓶颈。整条链路更像一个持续搜索过程而不是跑完一篇论文中的静态优化pass。2.2 为什么多智能体分工比单一编译器更有优势单一编译器比如TorchScript的早期实现最大的痛点是“所有规则都写在同一个代码库里”新增一种优化策略就要改编译器核心逻辑而且不同优化pass之间的交互非常复杂。多智能体的分工从架构上规避了这个问题每个智能体可以独立演进。我已经有了一个更好的Triton kernel模板不需要重写整个编译器只需要更新CodeGen智能体的策略库。失败隔离。CodeGen生成的kernel如果验证不通过Verifier直接打回不会影响其他已经完成的优化。自适应搜索。同一个模型在不同硬件上的瓶颈可能完全不同A100上可能launch开销是瓶颈华为昇腾上可能内存带宽约束更突出多智能体系统可以根据profiling结果动态调整搜索方向。2.3 多智能体不是“大模型跑一遍”关键在评估机制这里我要特别强调一点多智能体框架能不能落地关键不在每个智能体的“智力”而在智能体之间的通信协议和评估机制。如果Verifier给不出可靠的收益评估CodeGen就失去了反馈信号如果Profiler的瓶颈定位不够准后面所有智能体都在白忙活。实际系统中最常看到的做法是为每个候选优化方案维护一张评测表正确性是否通过、kernel耗时、显存占用、编译时间、鲁棒性换一个batch size是否仍然成立然后由调度层按加权分数决定最终执行计划。这种机制带来的好处是即使某个智能体的判断不完美只要验证闭环是可靠的整体系统仍然能收敛到较优解。3. 2.88倍加速的组成哪些优化贡献了最大收益我平时做推理服务优化时会先算一笔账在调整任何代码之前先用profiler把这个模型Eager模式下的时间分布拆出来分成几大类Python层调度开销、kernel启动开销、kernel计算时间、数据搬运时间、内存分配时间、kernel之间无法重叠的空档。这样做了之后“2.88倍”这个数字就不再神秘它本质上等价于把这六类开销里的绝大部分压掉之后的结果。3.1 第一个大头kernel启动次数怎么降下来Eager模式下一个卷积网络跑一次forward需要launch的数量等于算子数量加一些辅助操作。对一个ResNet-50来说这个数字在100个左右。而kernel launch本身就有一个固定成本——把kernel参数、grid/block配置从CPU传到GPU驱动层。如果把“kernel launch”类比成一次快递发货单个小算子的计算时间相当于实际路上的运输时间而launch开销相当于每次发货都要填面单、过安检。100个算子就是100次发货流程哪怕每个算子只花1微秒执行10微秒的launch开销也会让整体变成1000微秒。降低这个开销最直接的方式就是算子融合把多个算子合并成一个kernel一次launch完成多次计算。比如conv batch_norm relu合并成一个kernel三到四个算子的launch开销变成一次。一个融合充分的网络launch次数可以从100降到30甚至20。我推断PIKE这一类系统在2.88倍的贡献里算子融合带来的收益是第一位的。这不仅是经验判断也是基本的结构逻辑Eager模式最固定的开销就是逐算子调度而融合直接把调度次数降到最低。3.2 第二个大头缓存分配和中间张量的消除融合之后由于不需要在算子之间传递中间tensor内存分配次数也同步下降。在Eager模式下conv - relu - pooling这个序列至少产生两次中间tensor分配conv的输出、relu的输出融合后直接在kernel内部完成分配次数降到一次甚至为零如果输出可以被预分配buffer复用。对显存紧张或是需要高频推理的场景这一项的收益甚至超过kernel融合本身。因为连续分配释放会打断GPU流水线让SM的利用率降下来。消除中间tensor后kernel间的流水线更顺GPU的吞吐也能拉高。3.3 第三个大头CUDA Graph消除kernel launch的剩余抖动就算做了算子融合kernel的数量仍然有几十个。CUDA Graph把整张计算图捕获成一个graph然后通过一次graph launch完成所有kernel的提交。这个模式用在推理场景等于把成百上千的CPU端调度工作全部在GPU端以图结构编排好CPU不需要逐个launch。PIKE这类系统如果把CUDA Graph的捕获环节做得足够好自动处理动态shape、自动处理显存复用单单这一项就能让总延迟再降20%到40%。尤其对于batch size固定的在线推理服务CUDA Graph是所有优化手段里性价比最高的一项。我自己在项目里用CUDA Graph优化一个多分支的推荐模型延迟下降31%几乎零代码改动。3.4 2.88倍适合哪类模型算力密集度越低加速比越高需要明确的是2.88倍不是一个能套到所有模型上的常数。从标题看这个数据对应的应该是“有代表性的推理负载”而不是大模型的极端场景。一个规律我踩过之后记得很清楚模型计算量越小、算子越短、batch越小Eager模式的固定开销占比就越高优化空间越大加速比越接近2.88倍甚至更高反过来如果模型本身是满计算量的卷积网络连续跑很久kernel计算时间占绝对主导那么即使调度开销全部清零加速比也到不了2倍。所以拿到一个系统宣称的加速比首先要看它基准模型是什么。如果你的推理任务本身算力密集型已经很高那不要指望复现它的全部数字如果你的模型恰好是那种“算子琐碎、每层计算都很轻”的小模型那么加速比会接近甚至超过论文里的数字。4. 把PIKE的思路带进自研推理服务优先级明确的改造路线我知道多数人看完这种论文标题后最关心的问题还是我不可能立刻搭一套多智能体系统出来但能不能从它的思路上提炼出一些马上能做的事情能而且优先级非常明确。其实PIKE这套多智能体系统的本质逻辑就是“先定位瓶颈再针对瓶颈选优化手段”这个逻辑完全可以手工复刻只是它把它自动化了。4.1 第一步用profiler找到你的Top 10耗时算子先不要急着上任何优化工具先搞清楚你的模型在Eager模式下时间花在哪里。我用的是torch.profiler.profile每次都把activities打开CUDA相关的profiling同时记录CPU time和GPU time。核心看几个指标Self CPU timeCPU端调度耗时反映Python/dispatch/launch开销CUDA timeGPU端kernel实际耗时CUDA activity里kernel的launch次数每个tensor的alloc/free次数对应内存分配开销。这个过程相当重要。我见过一个项目团队花了两个星期在优化某个kernel实现上最后profiling发现那个kernel只占整体延迟的6%真正的瓶颈是一个在循环里反复执行的小张量拷贝。连问题都没定位对后面所有优化都是在做无用功。4.2 第二步按收益排序的优化手段清单完成profiling之后按照优先级把下面的优化手段逐个应用每做完一个就重新profiling确认收益开启torch.inference_mode这是成本最低的一步省去autograd相关的tracking开销。如果你还在用torch.no_grad()建议换成inference_mode两者在推理加速上的差异在Eager模式下实测可以差到5%以上。用torch.compile做无脑版本先直接model torch.compile(model)看收益。对多数常见模型这一两步就能拿到1.5到2.5倍的提升。如果编译失败或收益不明显说明模型里有动态shape或者自定义算子阻断了图优化这时候进入下一步。手动算子融合把最具连续性的算子组合conv bn act、linear act、multi-head attention内部的matmul softmax matmul写成自定义的Triton kernel或直接用已有的融合库。注意这一步只做高频热点别把整个网络都手工重写。CUDA Graph捕获如果你的模型输入shape固定、无动态分支直接调用torch.cuda.graphs.CUDAGraph捕获完整推理过程。这是收益最大也最容易实现的一项替换后推理延迟能再降20%到40%。服务侧连续batch与显存预分配在在线服务场景下确保请求能够连续执行batch内部不出现频繁的显存增长/释放。用静态显存池而不是每次请求都重新分配。下面给一个最简CUDA Graph迭代的代码骨架方便直接复用import torch class CudaGraphInference: def __init__(self, model, sample_input): # 预热确保caching allocator、cuDNN autotune状态就绪 for _ in range(10): model(sample_input) torch.cuda.synchronize() # 静态输入buffer要与捕获时完全一致 self.static_input sample_input.clone() self.static_output None self.graph None # 捕获graph g torch.cuda.CUDAGraph() with torch.cuda.graph(g): self.static_output model(self.static_input) torch.cuda.synchronize() self.graph g def infer(self, input_tensor): # 将真实输入拷贝到静态buffer再回放graph self.static_input.copy_(input_tensor) self.graph.replay() return self.static_output注意几个容易踩的细节捕获前必须保证分配器状态稳定预热的目的就在于此copy_的输入shape必须和静态buffer完全一致否则会触发重分配导致graph失效多stream场景下还要注意torch.cuda.graph不能和其他stream混用。4.3 第三步针对“多智能体”思路的轻量级借鉴如果想把PIKE的多智能体思路以手工方式沉淀到团队里建议维护一份“优化知识库”每个优化手段按以下字段沉淀适用模型类型、预期收益范围、实现复杂度、依赖条件、验证方式。接着定义好profiling与优化手段之间的映射规则热点是kernel launch次数过高对应算子融合。热点是内存分配频繁对应buffer复用或CUDA Graph。热点是kernel之间存在明显气泡对应stream并行或图融合。这么做一段时间后你的团队实际上就在用一套“规则智能体”来加速推理优化决策了——每个成员依据知识库针对不同场景选择策略。等到规则积累到一定量级再去尝试用自动化的智能体替代人工判断就有了基础。5. 复现和验证加速比时的三个典型误区最后这部分我特别想写因为这两年我在优化推理服务时发现很多人做的benchmark完全不可靠甚至会把一个负优化当成正收益记录下来。下面三个误区是我自己在实践中踩过、也看同事反复踩的5.1 预热不充分把CUDA缓存的冷启动成本算进“推理延迟”里PyTorch的caching allocator和cuDNN autotune都有“冷启动”阶段。第一次执行时cuDNN会跑多个候选算法做benchmark选择最优caching allocator的缓存也没建立起来此时测出的延迟会比稳定状态高很多甚至高50%以上。正确做法是统一预热10次以上torch.cuda.synchronize()后再记录时间正式计时时多次运行取中位数或均值并且至少跑50次以上。我个人的习惯是每次benchmark前先在单独进程里跑10次确认GPU频率稳定之后再进行正式计时。5.2 对比Eager模式时用了不同的精度或输出这类问题更隐蔽。我自己就犯过一次Eager模式下用FP32推理优化后不小心切到了FP16然后延迟确实降了接近一半看起来像“优化成功”实际上大部分收益来自精度降低。反过来也有风险FP16的kernel在某些老GPU上反而比FP32慢。正确做法是在benchmark代码里显式断言优化前后输出张量的dtype和shape一致并且对精度做一个校验比如max_abs_error和cosine_similarity。把精度校验写进自动化测试比人肉盯有效得多。5.3 只测batch1和小shape完全脱离线上真实情况推理加速的效果和batch size高度相关。batch1时launch overhead占比极高CUDA Graph和算子融合能带来2倍以上收益但batch增大到32以上时kernel计算时间占比大幅提升融合收益可能降到1.2倍。如果你的线上实际是batch16的推理却拿batch1的benchmark来证明优化有效那后面上线一定会被打脸。正确做法是同时测多组batch size和shape覆盖线上真实分布并且关注P99/P95延迟而不是只关注平均延迟。在线推理场景中长尾延迟对用户体验的影响远大于平均值。还有一个常被忽略的检查点优化后的推理代码必须跑在真实推理框架中验证整体延迟不要单独抽一个模型孤零零测。服务端的线程调度、CPU和GPU之间的数据搬运、前处理和后处理的耗时都会直接影响优化收益能否落到线上。6. 我自己在推理优化上的一些体会回到PIKE这个系统本身它的价值不只是“多了一个能加速2.88倍的框架”更重要的是它示范了一种做推理优化时应该具备的工作范式先精细度量再自动化搜索最后验证闭环。没有profiling就没有优化方向没有验证就没有优化结论这是我在实际项目里反复被证明的事情。如果你短期内没有条件引入完整的多智能体系统那就先把基础工作做扎实。把Eager模式的耗时构成摸清楚用上一节里提到的全套优化手段跑一遍大多数场景下你能拿到比2.88倍小一点但已经很可观的提升。而当你积累的优化规则越来越多自然就会理解PIKE这类系统为什么要用多智能体来承担这些工作——因为人工维护几百条优化规则的组合逻辑本身就是一件极其脆弱和费力的事情。最后分享一个实用的小技巧每次优化完成后别只记录“延迟降低了多少”要把完整的运行环境记下来包括GPU型号、驱动版本、CUDA版本、PyTorch版本、batch size和输入shape。因为这些因素里任何一个变了同样的优化手段可能换来完全不同的结果。我手上就有过一个案例同样的CUDA Graph优化A100下降31%V100只下降12%原因就是两个GPU的kernel launch架构差异。多记一点环境参数后续排查问题时你会感谢自己。