最近在试torch.compile跑一个7B的LLM做流式生成,用的贪心解码。发现不管是reduce-overhead还是max-autotune模式,首token延迟确实降了,但后续每个token的生成速度比纯eager模式慢了差不多30%。我理解编译应该对静态图有优化,但自回归生成每一步输入长度都在变,是不是这种动态shape场景根本不适合用torch.compile?还是说需要配合cudagraphs或者把KV cache的tensor固定shape才行?有没有大佬实际在生产环境里用编译模式跑过LLM推理的,求指点一下正确的调优姿势,谢谢!
PyTorch2.0编译模式在大模型推理时反而更慢了,是我打开方式不对吗?
全部回复
共 6 条动态shape确实会打断编译优化,试试把KV cache预分配好固定长度,配合cudagraphs应该能救回来。
动态shape确实白瞎了编译优化,试试把KV cache和输入padding到固定长度再上cudagraphs,能救回来不少。
固定shape才是关键,我这边跑通后速度反超eager了,但流式输出还是建议直接上vLLM那套。
动态shape确实是硬伤,你可以试试把KV cache提前pad到最大长度固定住shape,cudagraphs配合下会好很多。
我们之前跑13B也踩过这坑,后来干脆只在prefill阶段用compile,decode还是切回eager。
说实话你这情况挺典型的,动态shape在compile下会触发多次重新编译,反而把优化开销吃掉了。我之前试过把KV cache按最大长度预分配,然后给compile传dynamic=False,配合cudagraphs,token生成速度才勉强跟eager持平,但也没快多少。感觉torch.compile现阶段更适合训练或者batch推理这种shape稳定的场景,流式生成真不如直接上vLLM那套paged attention来得实在。你要是非得用compile,试试把输入padding到固定长度,或者干脆只编译prefill部分,decode保持eager,说不定能兼顾延迟和吞吐。
动态shape确实是硬伤,建议试试把KV cache预分配固定长度再配合cudagraphs,我这边能拉回不少性能。
说实话你这个现象挺典型的,我拿13B模型试过一阵子,最后发现torch.compile真正吃香的场景是那种固定batch、固定序列长度的训练或者预填充,一到逐token解码就拉胯。核心问题其实不在动态shape本身,而在于每次decode只有一次matmul,编译带来的kernel融合收益根本覆盖不了capture和dispatch的开销,尤其reduce-overhead模式下cudagraphs会强制把GPU work排成队列,反而打断了流式生成里原本可以pipeline的memcpy和compute。你提到把KV cache固定shape,这个方向是对的,但更关键的是得把整个解码循环包进一个大的compiled region,而不是每步都重新走一遍graph break,否则每次都得重新触发编译逻辑。我实际试过把past_key_values的max_length定死,然后配合torch.compile的dynamic=False,再把采样改成贪婪,确实能把慢30%拉回到接近eager,但也就持平,没赚到便宜。生产上我最后是放弃了compile,改用vLLM或者TensorRT-LLM那种专门做paged attention和连续batch的方案,那些才是为自回归量身定制的。你要是非在PyTorch生态里折腾,建议试试把decode阶段的qkv投影和输出层单独拎出来手动fuse,或者干脆用torch._dynamo的capture模式配合静态KV cache,但说实话性价比不高,不如换推理框架。你测的reduce-overhead首token降延迟我倒是没遇到,可能跟模型结构和gpu型号有关,方便说下你的具体配置吗?