最近在折腾LLM推理优化,看到大家都在吹torch.compile能白嫖30%性能,就把手里的Llama-3-8B QLoRA微调脚本改了改。结果train_step时间不降反升,显存倒是多吃了2G。查了文档说dynamic=True能处理变长输入,但我序列padding到512后shape固定了啊?还有那个mode='reduce-overhead'和默认的inductor有啥本质区别?另外给attention层加了SDPA后,编译时老报“Triton codegen failed”,回退到eager又变慢。有没有老哥在A100上实际验证过什么场景下编译收益最大?还是说我这种小batch(4)压根不适合开编译?真心求教,别让我再瞎试了。
PyTorch 2.0编译模式到底该不该开?试了几天反而更慢了
全部回复
共 72 条小batch就别折腾编译了,inductor在batch=4上纯属负优化,试试CUDA graphs或者直接砍掉padding。
小batch下编译开销经常盖过收益,我这边A100上也是batch 4跑8B模型基本没赚头,反而动态shape的guard检查拖后腿。mode='reduce-overhead'主要靠CUDA Graph省kernel launch,但QLoRA本身算子碎、还有dequant,图捕获容易失败。Triton codegen那个报错大概率是SDPA后端没对齐,试试设torch.backends.cuda.enable_flash_sdp(True)或者换math后端看能不能绕过去。真想验证收益建议先把compile关掉做baseline,再单独compile backbone,别整个train_step一起包进去。
编译模式确实不是无脑开就行的,得看场景。我这边A100上跑7B和13B的推理,batch小于8的时候torch.compile基本没收益,有时候还倒退,因为inductor的kernel launch开销在小batch下压不下去。你把batch提到16以上再试试,或者换成静态shape加mark_dynamic只标真正变长的维度,别让整个图都走dynamic路径。reduce-overhead主要靠CUDA Graph省launch,但它对显存占用和输入地址稳定性要求高,QLoRA训练里optimizer step和梯度累积很容易破坏graph捕获,反而触发反复重编译。SDPA那块Triton报错大概率是head_dim或者mask形状没对齐,你可以先试试mode="max-autotune-no-cudagraphs"排除graph干扰,或者单独把attention用eager跑其余部分编译。小batch训练场景下编译收益本来就有限,不如先确认下是不是数据加载或者allreduce成了瓶颈。
小batch配QLoRA确实容易踩这个坑,torch.compile对计算密集的大矩阵乘收益才明显,你这场景瓶颈大概率在反量化kernel和数据搬运上,编译反而多了一层调度开销。显存涨2G基本就是inductor把中间结果缓存下来的代价,正常现象。dynamic=True对你padding固定后确实没意义,它主要是给真正变长shape用的,开着反而会关掉一些静态shape优化。reduce-overhead本质是走CUDA Graph去压kernel launch开销,小batch下launch占比高才可能有效,但配LoRA这种带控制流的模型经常graph capture失败,回退eager就不奇怪了。SDPA那个Triton codegen failed八成是head_dim或者mask形状没对齐,可以试试先单独compile attention模块定位。A100上我实测收益最大的还是pretrain那种大batch+固定seqlen的场景,推理侧要开也得配合cudagraph和静态KV cache,不然省的那点还不够折腾。
小batch下编译开销确实很难摊薄,A100上我试过8B模型batch=4,inductor默认模式基本没收益,reduce-overhead反而因为CUDA graph捕获把显存吃得更狠。你序列定长512还开dynamic确实没必要,试试static shape加mode="max-autotune",但首次编译会等挺久。Triton codegen挂多半是SDPA和编译后端版本对不上,可以单独把attention层用torch._dynamo.disable包起来先跑通。
小batch场景下编译收益确实很有限,你这情况挺典型的。torch.compile最吃香的是大batch、固定shape、反复跑同一张图那种推理serving,微调这种每步都可能有梯度累积和优化器状态变化的反而容易被overhead吃掉。你padding到512但attention mask是不是还是变长的?mask形状一变就可能触发recompile,可以打个torch._dynamo.explain看看graph break在哪。reduce-overhead主要靠CUDA graph省kernel launch,但QLoRA的dequant和adapter层操作太碎,graph捕获经常失败,回退后反而多一层开销。SDPA那个Triton报错八成是flash attention后端和你的head_dim或dtype对不上,试试强制走math后端先排除一下。A100上我见过收益最明显的是固定seq_len的纯推理,batch至少16以上,编译一次跑几千步才回本。你这种小batch微调,老实说eager加gradient checkpointing可能更实在,别被那30%的宣传带偏了。
小batch确实很难吃到编译红利,inductor那套kernel融合在计算密度低的时候overhead反而更明显。你序列固定到512了可以试试dynamic=False加mode='max-autotune',reduce-overhead主要走cudagraph省launch开销,但跟QLoRA的dequant容易冲突。Triton codegen失败大概率是SDPA跟低精度算子搅在一起了,试试关掉flash attention走math backend看看还报不报。A100上我这边batch至少上到16、序列1k以上才看到明显收益,不然还不如老实eager。
小batch下编译开销根本摊不平,不如先跑eager测baseline,Triton报错可以试max-autotune-no-cudagraphs。
小batch下编译开销根本摊不薄,QLoRA本身又在频繁换kernel,收益能正才怪。
小batch下torch.compile确实容易翻车,我这边A100上跑推理也是,batch小于8时基本没收益,编译开销都摊不回来。dynamic=True对你padding到512的情况本来就不该开,反而会触发更多guard检查拖慢速度。mode='reduce-overhead'主要靠CUDA Graph省kernel launch,但你QLoRA训练里有反向和优化器更新,图捕获经常失效,还不如默认inductor稳。Triton codegen failed大概率是SDPA和某些head_dim组合没走通,可以试试设torch.backends.cuda.enable_flash_sdp(False)排除下是不是flash attention后端的问题。
写得挺好,建议补充一些性能数据。
这个思路不错,收藏了。