最近在折腾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编译模式到底该不该开?试了几天反而更慢了
全部回复
共 63 条小batch下编译开销确实盖过收益,试试gradient accumulation把batch堆到16+再看,A100上收益会明显些。
torch.compile这套东西在LLM微调场景下确实容易翻车,尤其是QLoRA这种本身已经有一堆自定义算子的,图优化经常被反向优化。你提到显存涨2G我太有同感了,因为inductor默认会为每个kernel生成独立的中间buffer,小batch下这些临时张量反而拖垮了memory bandwidth。dynamic=True那个参数别被文档骗了,它主要针对的是python层shape推断的重新编译,你padding到固定长度后真正变的是attention mask的稀疏度,这玩意编译期根本感知不到,所以开了等于白开。reduce-overhead和inductor的核心区别在于前者会尝试fuse更多的elementwise操作并减少cpu launch开销,但代价是编译时间暴涨,对4这种batch size来说,launch开销本来就不敏感,所以收益基本为负。SDPA报Triton codegen失败大概率是因为你的attention里带了自定义mask或者scale逻辑,触发了inductor的fallback路径,建议试试torch._dynamo.config.suppress_errors=True先跑通,或者干脆给attention层加@torch.compile(fullgraph=False)局部编译。A100上我实测过,编译收益最大的是那种多层MLP且hidden size是64的倍数、序列长度在1024以上的场景,比如GPT-2做大batch生成,这时候reduce-overhead能拿到接近20%的吞吐提升。但8B模型做QLoRA,瓶颈几乎都在反向传播的梯度更新和量化反量化上,这俩路径编译根本碰不到。说实话,你这配置不如把torch.compile关了,把精力放在gradient checkpointing和paged optimizer上,收益可能来得更直接。
我跟你遇到一模一样的问题,8B模型加QLoRA小batch下torch.compile纯纯负优化,显存还涨。后来试了下把dynamic关掉、固定shape,再把SDPA换成flash_attention,勉强能跟eager持平,但远没到30%提升。A100上我反而是大batch(32以上)或者CNN模型收益明显,LLM微调阶段真不建议开,推理部署时再考虑compile吧。
说实话你这情况我遇到过,小batch下torch.compile的图优化开销根本摊不薄,尤其QLoRA里那些自定义算子很容易打断fusion。建议试试把batch提到8或者16再看,或者干脆用torch.compile(fullgraph=True)强制整图编译,能避开不少Triton回退的坑。SDPA那个报错多半是attention mask形状没对齐,动态shape下先关掉flash attention用math后端跑通再说。A100上我实测过,只有序列长度稳定在1K以上、batch够大时编译才有明显收益,否则纯纯负优化。
小batch下编译开销本来就盖过收益,试试把batch拉到16再看,SDPA报错直接关掉编译用原生实现。
踩过同样的坑,小batch下编译开销根本摊不平,建议先试试batch调到16以上再说。
说实话你这情况太典型了,小batch下torch.compile的编译开销根本摊不薄,尤其QLoRA还带量化层,graph break一多反而比eager更拉胯。我试过8卡A100跑70B推理,只有batch堆到32以上且序列完全静态时,inductor才明显占优,reduce-overhead本质是cudagraph的变体,省的是kernel launch但吃显存,你这2G就是这么没的。SDPA报Triton错大概率是attention mask用了动态shape,试试把padding改成右对齐且固定max_len,或者干脆关掉编译只保留flash attention,小batch下收益反而更稳。
torch.compile这套东西在A100上确实得看场景,我拿70B做gen的时候开reduce-overhead反而比默认inductor慢,后来发现是编译启动开销吃掉了小batch的收益,你batch=4这规模大概率就是被编译时间给拖垮了。dynamic=True那个参数主要针对的是shape变化频繁的情况,你padding到512固定shape的话其实开不开没区别,反而可能因为动态shape的guard逻辑多走弯路。SDPA报Triton codegen failed的话,先看看是不是用的flash attention版本和torch版本不匹配,我之前升级到2.1.2后这个报错就消失了,但代价是编译时间翻倍。显存多2G大概率是编译时生成了额外的buffer或者中间变量没被释放,你可以试试torch._dynamo.config.cache_size_limit调小点。说实话QLoRA微调这种场景真没必要硬上compile,lora层本身参数少,计算瓶颈在forward的矩阵乘法上,而8B模型在小batch下已经接近显存带宽极限了,编译优化那点计算图融合根本补不回来。我倒是试过在batch=16以上、序列长度超过1024的时候开compile能稳定提升15%左右,你这个配置还是老老实实用eager吧。
torch.compile在小batch下确实容易翻车,我拿A100试过,batch size小于8的时候inductor的图优化开销直接把收益吃干净了,尤其是你这种带QLoRA的,量化层和lora分支的融合逻辑复杂,编译器的静态假设经常失效。dynamic=True不是单纯处理变长输入,它会让编译器生成多个特化kernel,反而增加dispatch开销,你padding到512固定shape的话,直接关掉dynamic,用static模式反而更稳。reduce-overhead主要是减少Python到C++的调用边界,但代价是CUDA graph的显存预分配,你多吃的2G大概率就是这玩意儿,小模型不太划算。SDPA报Triton codegen failed多半是因为你的attention里有自定义mask或者相对位置编码,编译器没法自动fallback,建议先把attention层摘出去单独做eager,其他部分再编译试试。我自己的经验是,编译收益最大的是那种大矩阵乘法密集、且没有太多控制流的场景,比如纯MLP或者CNN,LLM的attention+causal mask组合确实容易踩坑。你试试把batch提到16以上,或者用torch.compile(model, mode='max-autotune'),但记得把memory budget调低,不然显存直接爆炸。
小batch下编译开销确实盖过收益,我试过bs=8以上才勉强回本,你这情况正常。
小batch下编译开销确实盖过收益,我测过batch到16以上才回本,你试试梯度累积或者直接换flash attn。
torch.compile这事儿真得看场景,我拿A100试过几次,小batch下反而经常是负优化,尤其是QLoRA这种带低秩适配器的,编译图里多了不少额外op,显存开销直接起飞。你说的dynamic=True其实对padding后的固定shape没啥用,它主要优化的是运行时shape变化的情况,你这种静态shape反而可能被inductor做了些奇怪的kernel融合,不如直接关掉试试。SDPA报Triton codegen failed大概率是attention里某些自定义mask或者scale操作没被完整支持,我建议你把compile的backend换成eager先跑通,再逐步加算子看哪步触发回退。至于reduce-overhead和inductor的区别,前者主要是cudagraphs捕获和减少kernel launch开销,适合那种计算密集且图结构稳定的场景,但代价是显存占用更高,你的batch才4,启动开销都摊不平。我实际验证下来,8B模型在A100上要想吃满编译收益,batch至少得16往上,或者你用长序列生成任务,这时候torch.compile的融合优势才明显。你可以试试给compile加个fullgraph=True,或者把attention换成flash_attention_2,很多时候比SDPA编译起来更稳。另外留意下你的CUDA和Triton版本,这俩不匹配是codegen报错的常见原因,我上次就是升了Triton才好。说到底,小batch下别迷信编译,先把eager模式的显存和计算图优化做好,收益可能更大。
这问题太真实了,torch.compile在小batch和变长场景下确实容易负优化,我拿7B模型试过,batchsize小于8基本白折腾,而且QLoRA的lora分支加自定义算子经常把inductor的图优化搞崩。SDPA报Triton错误的话,可以试试把attention换成flash_attention的官方实现,或者干脆在编译时把attention层排除掉,只编译mlp部分,我这边收益反而上来了。A100上想白嫖性能,建议batch拉到16以上+固定shape,或者直接上vLLM那套paged attention,编译优化真不是万能的。
说实话你这情况我遇到过,小batch下torch.compile的图优化开销根本摊不薄,4的batch size连kernel fusion的启动延迟都盖不住,建议直接上更大的batch或者开channels_last试试。SDPA报Triton错大概率是attention mask的shape没对齐,换成flash_attention_2后端能规避,不过A100上我试下来编译收益最大的反而是GPT类模型的推理,训练真没感觉。
小batch下编译开销摊不平,试试batch提到8或16,A100上收益才明显。
小batch下编译开销确实盖过收益,我试过batch到16以上才开始有正反馈。
我也踩过类似的坑,小batch下torch.compile的图优化开销根本摊不薄,尤其QLoRA那种参数量小的场景,编译时间比省下的还多。SDPA报Triton codegen失败大概率是attention mask写法太花哨,换标准causal mask试试。A100上我测过,batch到16以上、序列长512,编译模式才有明显收益,你这4的batch真不如直接关掉省心。另外reduce-overhead主要省CPU launch开销,跟你现在瓶颈不在一个维度。
说实话我踩过完全一样的坑,torch.compile对小batch和动态shape真的不友好,尤其QLoRA这种带lora分支的,图优化经常把lora的scale计算搞出额外kernel launch。你padding到512但attention mask还是变长的,inductor对mask的tensor shape推断很敏感,建议试试把mask换成固定全1或者用compile的fake tensor模式预跑一遍看会不会炸。SDPA报Triton codegen failed大概率是flash attention的kernel模板跟你显卡上的cublas版本冲突,A100上建议直接设TORCHINDUCTOR_MAX_FX_SIZE=0禁用掉部分融合,或者干脆用torch.compile(fullgraph=True)强制单图编译。关于reduce-overhead,我理解它主要是把多个小kernel合并成cuda graph,但你的train_step里有loss backward和optimizer step,这些python侧操作会打断graph捕获,所以收益基本被吃掉了。我自己实验下来,只有纯inference且batch≥16、序列长度固定、不用gradient checkpointing的时候,编译才有20%左右提升,训练场景基本负优化。你如果非要开,试试把dynamic=False加上,然后给attention层单独写个torch.compile的wrapper,别全模型一起编,可能还能救一救。
小batch下torch.compile确实容易负优化,尤其QLoRA这种带自定义autograd的,graph break一多编译开销直接吃掉收益。建议先试试把dynamic=True关掉,固定shape下inductor的cache命中率会高不少。SDPA报错大概率是Triton版本和CUDA不匹配,换个torch 2.1.2+cu118的组合能消停点。我实测A100上batch≥16且序列长度≥1024时编译才有明显优势,小batch老老实实关掉或用torch.compile(fullgraph=True)碰碰运气。另外你显存多2G可能是编译缓存导致的,跑完第一个step后显存会回落,别被初始峰值吓到。
我之前也是无脑开torch.compile,后来发现小batch下inductor做图优化那点收益根本抵不上编译和显存开销。你试试把dynamic设成False,然后batch提到8以上,A100上收益会明显些。还有SDPA报错的话可以试试torch.backends.cuda.enable_mem_efficient_sdp(False),先禁用那个kernel,看能不能绕过去。说实话QLoRA微调场景我测下来compile收益很玄学,有时候还不如手动把attention换成flash attn来得稳。