最近在给公司做LLM推理优化,看到PyTorch2.0的torch.compile宣称能提升30%-40%性能,就试了试。本地benchmark确实快了,但部署到生产环境(T4 GPU,batch动态变化)后发现两个问题:一是首次编译时间太长,大概要十几秒,预热的warmup策略没找到好的实践;二是配合vLLM或TensorRT-LLM时,compile反而和这些框架的CUDA graph冲突,偶尔报显存碎片错误。想请教各位,你们在生产环境里真的会用torch.compile吗?还是说只用来做训练?如果是推理,有没有和vLLM搭配成功的案例?或者说干脆别折腾,直接换ONNX或TensorRT更省心?刚接触这块,有点迷茫,求指点。
PyTorch2.0的compile到底该不该用在推理服务里?
全部回复
共 42 条说实话推理这块我直接弃了compile,vLLM自己那套graph已经够折腾了,再加一层纯属找罪受。
T4上遇到显存碎片基本是无解的,尤其batch动态变化时CUDA graph的capture一次就废了。我这边试过在A100上勉强能用,但收益也就10%出头,远没到宣传的30%。建议直接TensorRT,虽然麻烦点但至少可控,vLLM那套优化已经够吃满T4了,compile真没必要硬塞进去。
老实说生产环境就别折腾compile了,跟vLLM抢显存真没必要,直接TensorRT省心。
说实话我们之前也踩过这个坑,T4上torch.compile的收益在动态batch下会被编译开销吃掉大半,后来干脆只在固定shape的离线批处理场景用。跟vLLM搭配的话,个人建议别硬融,vLLM自己的paged attention和CUDA graph已经优化得很好了,强行套compile反而容易出内存碎片问题。如果追求极致性能,还是直接上TensorRT吧,就是构建引擎那步比较费时间,但线上稳定性比compile靠谱多了。
说实话我试下来感觉compile在动态shape场景就是给自己找事,T4上那点收益抵不过warmup和显存碎片的折腾。现在生产环境要么直接TensorRT,要么就老老实实关掉 compile让vLLM自己管CUDA graph,两边都省心。倒是训练阶段用compile挺香,反向传播的加速比推理明显得多。你要是非得在推理里用,建议固定batch size并且预跑几十个step把算子缓存住,能缓解一部分问题,但别指望和vLLM共存。
说实话我试过一圈下来感觉torch.compile更适合训练或者静态shape的离线推理,动态batch下跟vLLM抢显存管理权确实容易出幺蛾子。你要是主要跑LLM服务,不如直接上TensorRT,T4上优化得比compile狠多了,虽然转换时也折腾但至少不会线上炸。不过如果你有那种超长prompt的请求,compile的图优化在某些场景下还是能捡点便宜的,关键看你们业务shape分布稳不稳。
生产环境真不建议硬上compile,跟vLLM抢CUDA graph纯属给自己埋雷,我们最后也退回TensorRT了。
之前踩过同样坑,动态batch下那点性能提升根本盖不住显存碎片的随机性报错,老实换TensorRT省心多了。
说实话我踩过一模一样的坑,T4上torch.compile那个首次编译时间直接让我们的健康检查超时,后来干脆只在训练阶段用,推理全切成TensorRT了。不过我觉得你遇到的CUDA graph冲突大概率是版本兼容问题,我们当时在A100上试过vLLM+compile,只要把max_batch_size固定住,并且用torch._dynamo.mark_dynamic避开动态shape,基本能稳定跑,但T4上显存碎片确实无解。我的建议是别纠结compile,vLLM本身对T4的优化已经很到位了,你就算把compile调通,收益可能也就5%以内,但运维复杂度翻倍。ONNX那条路也慎走,LLM的attention mask和KV cache在ONNX里表达起来特别别扭,除非你只做纯decoder的静态推理。真要榨性能,不如直接看TensorRT-LLM的paged KV cache和in-flight batching,T4上实测比vLLM默认配置能再快15%左右,就是构建engine时得把动态shape的range设宽一点。另外你提到的warmup问题,我见过有人用同一个shape的假数据先跑几十个step,然后把编译后的so缓存下来,但跨机器还是得重新编,所以生产环境基本没人这么干。
T4上折腾compile真不如直接上TensorRT,动态batch下CUDA graph那套冲突太真实了。
生产环境求稳,vLLM配TRT才是正解,compile留给自己写demo玩吧。
说实话我跟你遇到一模一样的坑,T4上动态batch加torch.compile基本就是负优化,编译时间都能赶上几个请求了。我后来干脆放弃了,推理全走TensorRT,训练才用compile,两边互不打扰。vLLM那边官方其实也建议关掉torch.compile,他们自己的CUDA graph已经做了不少优化,叠上去反而容易出内存碎片。你要是模型结构比较固定,试试把batch padding到固定几个档位,配合TensorRT的动态shape,比折腾compile省心得多。
说实话我试过一轮下来感觉torch.compile在动态batch场景就是给自己找事,T4上那点显存本来就紧张,跟vLLM抢graph资源得不偿失。我现在生产环境干脆直接走TensorRT,虽然转换麻烦点但稳定性好太多,compile还是留给训练阶段调模型结构用吧。
vLLM那边其实官方也明确说过支持compile,但实际踩坑的人不少,主要就是显存碎片和graph捕获时机对不上。你要是非要用,建议固定batch size或者干脆把max_num_seqs锁死,牺牲点吞吐换稳定,不过这样跟TensorRT的优势就拉不开了。
我这边倒是见过有人在离线批处理场景用compile吃到红利的,但在线推理真没必要硬融。你纠结的那十几秒编译时间,换个思路直接用torch.jit或者纯CUDA算子重写热点层,效果可能更直接,调试成本还低。
直接上TensorRT吧,compile那点提升在动态batch下真不够折腾的,vLLM都自带图优化了。
说实话我们团队也踩过类似的坑,T4上compile的收益在动态batch下基本被编译开销和显存碎片吃掉了。现在生产推理基本就是vLLM原生的CUDA graph,torch.compile只在训练和离线实验里用。你要是想省事,还是直接TensorRT吧,跟vLLM配合的坑少很多,我们之前试过把compile关掉反而更稳。
我后来发现一个折中方案,就是固定batch size并手动做一次warmup,把编译后的graph缓存下来,但这样部署流程会变复杂,而且模型一更新就得重新来一遍。如果你不是特别依赖动态shape,那ONNX Runtime的TensorRT EP可能比硬啃compile更实际,至少生态成熟不少。
想问问你那个显存碎片错误是出现在多次重编译之后吗?我们当时是把torch.backends.cuda.enable_cudnn_sdp设为False才勉强压下去,但性能又掉回原样了。
生产环境还是别硬上compile,跟vLLM打架得不偿失,老老实实TensorRT更省心。
T4上动态batch就别指望compile了,预编译那十几秒够你重启好几轮服务了。
说实话我试下来感觉torch.compile更适合训练阶段,推理这边跟vLLM的CUDA graph重叠得太厉害,T4上显存本来就紧,折腾半天收益不如直接TensorRT来得省心。不过你要是batch特别稳定、不走vLLM那套,纯自己写serving的话compile还是能用的,就是warmup得自己造数据跑几遍,别指望官方给现成方案。我现在的做法是小模型用TensorRT,大模型干脆就原生torch加动态shape,省得两头受气。
说实话我们这边也踩过类似的坑,T4上compile的收益本来就被显存带宽卡着,动态batch更是把它的graph优化吃得死死的。现在推理管线基本是vLLM+自定义kernel,compile只留在训练侧做实验用。你要是非要在生产里用,建议把warmup放到模型加载阶段,用固定shape跑一遍再切动态,但跟vLLM的冲突真没找到完美解法,除非你愿意放弃它自带的continuous batching。ONNX那条路至少稳定,TensorRT调好了也不差,就是迭代麻烦点。
说实话你踩的坑我基本都踩过一遍,最后结论是torch.compile在LLM推理这条路上性价比真不高。T4这种卡本身显存就紧,CUDA graph和动态shape的冲突几乎是必然的,我试过把batch固定成8的倍数勉强能跑,但一旦线上流量抖动直接崩给你看。vLLM那边其实自己有paged attention和continuous batching,它内部已经做了不少kernel fusion,你再套一层compile反而干扰它的内存规划,碎片问题我也遇到过,最后只能把compile关掉。
我现在生产环境就是两条路:要么纯vLLM跑FP16或者INT8,稳定省心;要么对延迟极度敏感的模型直接上TensorRT,虽然构建时间长一点但推理时没有幺蛾子。ONNX我反而用得少,主要是LLM动态shape支持太弱,除非你模型结构特别规整。训练端用compile确实香,尤其大batch下显存和速度都有改善,但推理服务要的是确定性,不是benchmark上的漂亮数字。
你要是实在想用compile,我建议只对attention或者mlp的某个子模块做局部编译,别整个模型套,然后warmup的时候多跑几个不同的batch size组合,把编译缓存提前触发掉。不过说实话,有这个调参时间不如去调vLLM的scheduler参数,收益更直接。你那边如果模型是MoE结构的话,情况可能又不一样,不过我还没试过,有点好奇。
说实话我觉得你遇到的情况挺典型的,torch.compile在推理场景里确实有点“鸡肋”——训练时它那个图优化能把反向传播省不少,但推理时尤其是像vLLM这种已经自己做了CUDA graph和内存池管理的框架,你再去套compile就属于重复造轮子,反而容易把显存生命周期搞乱。我之前在A100上试过类似组合,报错倒是没你这么频繁,但性能提升几乎可以忽略,还白搭了十几秒的编译时间,后来直接放弃。如果你是纯用PyTorch自己写serving,不用vLLM,那compile在静态shape下还能有点用,但一旦batch动态变化,它那个recompile的代价就完全抵消收益了。我个人建议推理这块还是老实走TensorRT或者ONNX Runtime,特别是T4这种卡,TensorRT的优化空间比compile大得多,官方对动态shape的支持也成熟。至于warmup,我见过有人用固定batch的伪输入先跑一遍再切真实流量,但总觉得这方案太脆弱,生产环境谁愿意搞这么麻烦。想问问你最后是换成TensorRT了,还是干脆保留原生PyTorch没做额外优化?毕竟有时候稳定比那30%的性能更重要。
说实话你遇到的情况我太熟了,T4上batch动态变化基本就是compile的噩梦,预编译那十几秒在线上根本等不起,更别说跟vLLM的CUDA graph抢显存这事,我这边报错比你描述的还频繁,后来干脆把torch.compile彻底从推理链路里摘出去了。现在我的做法是训练阶段用compile加速,推理直接走TensorRT,虽然转换麻烦点,但胜在稳定,而且T4这种卡上TensorRT的优化空间其实比compile更可预期。不过我也见过有同事在A100上用compile配合vLLM的静态shape模式跑通了,但那是把batch固定到8,牺牲了灵活性换性能,感觉不太适合我们这种流量波动大的场景。倒是想问问你,试过把compile的mode调成reduce-overhead或者max-autotune吗,有些时候换模式能躲掉显存碎片问题,但代价是编译时间会再翻倍。反正我现在的结论是,除非你推理服务的shape完全静态,否则别在生产环境折腾compile,ONNX和TensorRT至少社区踩坑案例多,出问题能找到解决方案。
生产环境还是别折腾compile了,跟vLLM抢显存真不值当,直接TensorRT省心多了。
实话说,这玩意儿跟CUDA graph八字不合,试过几次就放弃了,还是老老实实分开用吧。