最近在折腾用 Llama 2 7B 做微调,看到 PyTorch 2.0 的 torch.compile 吹得很厉害,说能白嫖 30%-50% 的加速。但我试了下在单卡 A100 上跑 LoRA,torch.compile(mode="reduce-overhead") 反而比原生 torch 慢了一截,而且第一次编译要等好久。我用的 deepspeed stage2 加 bf16,是不是大模型场景下编译加速不明显,还是我哪里配错了?大佬们有没有实际对比过训练吞吐的?另外,编译后的显存开销好像也变大了,这正常吗?真心求教,不想把时间浪费在玄学调优上。
PyTorch 2.0 编译模式在大模型训练中真的比原生快很多吗?
全部回复
共 165 条单卡A100上LoRA本来计算密度就低,compile那点优化还不够摊编译开销的,deepspeed图优化还容易打架。
说实话我也是踩过这个坑的,reduce-overhead在A100上对LoRA这种小参数量更新反而容易因为编译开销和kernel融合不到位拖慢速度。我后来试了mode="max-autotune"配合fullgraph=True,吞吐才勉强比原生快个10%左右,但显存确实会多个2-3G,感觉是CUDA graph缓存占的。你deepspeed stage2的话,建议把编译范围限定在backbone上,LoRA层别编译试试,另外checkpointing配合编译有时候会有奇效。反正我个人觉得现阶段torch.compile对7B这个规模收益真没宣传的夸张,还不如花时间调下flash-attention和梯度累积步数。
说实话你这个问题我太有同感了,自己拿7B模型在A100上折腾过好几轮,torch.compile对LoRA这种小改动量的场景确实经常是负优化,尤其是deepspeed stage2和bf16叠一起,图优化和梯度分片之间会互相打架,显存开销变大也正常,因为编译会保留更多中间张量做算子融合。我试下来,反而是纯全参数微调或者大batch时,compile的加速才明显,能到20%左右,但远没有官方吹的那么玄乎。你要是真想省时间,不如把精力放在gradient checkpointing和attention的kernel选择上,比如flash-attn2配xformers,我这边实测比compile稳定多了。另外你那个reduce-overhead模式,对动态shape特别敏感,LoRA的adapter如果没固定seq len,编译缓存会频繁失效,建议改成mode="max-autotune"再试一次,但别抱太大期望。还有个小细节,编译前把torch._dynamo.config.suppress_errors设为True,能避免某些算子回退导致的隐性开销,但真提速还得看具体模型结构。
同感,LoRA场景下torch.compile收益确实不明显,我试过7B模型加deepspeed stage2,编译后吞吐基本持平,有时候还掉点。你试试mode="max-autotune"加fullgraph=True,至少编译开销能摊薄一点,但别指望30%那种提升。显存变大正常,编译会生成额外buffer,尤其reduce-overhead模式特别吃显存,我建议小batch时直接用默认模式反而稳。这玩意儿更适合大batch纯训练,比如从头预训练或者全参微调,LoRA这种轻量场景真的没必要折腾。
说实话你这个现象我见过挺多次的,reduce-overhead这个模式本身就更偏向于小batch、动态shape的场景,大模型训练里反而容易因为编译带来的额外内存占用和kernel调度开销得不偿失。我之前在7B上试过,如果不开deepspeed,纯原生DDP加torch.compile还能看到一点点收益,但一旦上了stage2,通信和显存优化逻辑跟编译器的算子融合经常打架,吞吐不降就已经算不错了。你提到显存变大,这太正常了,compile会为每个算子生成多个专用kernel,还要存中间tensor的元数据,尤其在activation checkpointing没配合好的时候,峰值内存能多个5%到10%。建议你试试mode="max-autotune"配fullgraph=True,然后把deepspeed的offload关掉,batch size调小一点,看看能不能把编译的固定开销摊薄。另外第一次跑那几分钟的编译时间,可以提前用微调数据的一个小batch做warmup,把cache保存下来,后面就能跳过。不过说实话,你要是追求稳定性和省心,Llama这类模型用原生torch加flash attention和xformers,调好gradient checkpointing,性能一点都不差,编译那点加速在7B这个规模真不值得折腾。
说实话你这情况我太熟了,之前我也在7B上踩过坑,torch.compile那套reduce-overhead对LoRA这种小参数量更新反而容易增加调度开销,尤其deepspeed stage2本来就有自己的通信优化,两边叠加经常负优化。我后来对比过,纯原生加flash attention2加xformers的baseline,在单卡A100上比torch.compile稳定快10%左右,但编译模式在batch size拉到8以上或者做全量微调时优势才明显。显存变大是正常的,因为编译会生成额外中间缓存和CUDA graph上下文,尤其第一次编译那会儿峰值会飙不少,建议你试试mode="default"配合fullgraph=False,有些场景下反而更稳。还有个小坑,你bf16的话记得把torch.autocast范围包对,不然编译捕捉到混合精度的动态shape会重新触发过多recompile,那才是真玄学。反正我现在的结论是,小规模实验别折腾编译,直接原生跑,等真要上多卡或大batch再考虑,不然光调编译参数的时间都够跑完好几个epoch了。
我也踩过类似的坑,尤其是第一次编译的等待时间确实劝退。后来发现torch.compile对动态shape和某些自定义算子支持一般,LoRA这种注入额外分支的场景可能反而触发graph break,开销就上去了。显存变大倒是正常,编译会保留一些中间buffer和额外kernel,但你这情况建议试试mode="max-autotune"或者关掉cudagraphs,有时比reduce-overhead更稳。另外如果主要瓶颈在通讯而不是算力,编译收益确实会被稀释,deepspeed stage2下优先看通信优化可能更实际。
我之前在7B上试过类似的组合,deepspeed加bf16下torch.compile确实容易负优化,尤其是reduce-overhead模式本来就更适合小batch推理。你可以试试mode="max-autotune"或者干脆关掉编译,把精力放在gradient checkpointing和attention实现优化上,收益更直接。显存变大我遇到过,因为编译会保留一些中间buffer,属于正常现象,但要是大了10%以上就得查查是不是和deepspeed的zero阶段冲突了。另外想确认下你用的是不是最新版torch,2.1之后对llama类模型的编译支持改善挺多的。
说实话你这个问题我踩过一模一样的坑,先说结论:大模型场景下torch.compile的提升远没有宣传的那么神,尤其配deepspeed的时候。我自己的经验是,单卡小batch(比如bs=1或2)跑LoRA,编译模式反而容易因为图捕获和算子融合的额外开销拖慢速度,你那个reduce-overhead模式对显存要求更高,它要牺牲一部分显存换CUDA graph的稳定性,所以占用变大是正常的。
关键问题可能出在deepspeed stage2和torch.compile的兼容性上,两者在算子重写和梯度处理上会打架,导致很多优化没生效。我后来试过把deepspeed关掉,纯用HF的accelerate加bf16,compile反而快了一些,但也就10-15%的提升,远达不到30%。另外你试过mode="max-autotune"吗?它虽然编译时间更久,但自动调优出来的kernel在A100上通常比reduce-overhead强,尤其对attention那块。
还有个小坑:如果你用了flash-attention或者xformers,torch.compile会尝试重新融合它们,经常出诡异问题,建议先关掉这些外部算子试试。显存变大这个事,我反而觉得是正常的,因为编译后的kernel会保留一些中间buffer,你观察一下是不是峰值涨了5-10%,如果超过这个量级就得查查是不是deepspeed的offload和graph冲突了。
最后说句实话,如果你不是调优到极致追求那十几个百分点,大模型微调场景真没必要折腾compile,投入产出比太低。把精力放在数据质量、学习率调度、梯度累积策略上,收益大得多。要是真想试,建议用最新版torch 2.1+,2.0的bug确实多,而且注意别同时开deepspeed和compile的算子融合,二选一。
正常,小模型+LoRA场景编译收益本来就不大,reduce-overhead更适合大batch跑满算力。
编译开销在大模型上确实容易被放大,尤其deepspeed本身就有优化,收益自然不明显。显存变大是因为编译保留了额外buffer,正常现象。
同感,我在7B全参微调上也试过,reduce-overhead配合deepspeed反而有额外显存开销,后来换mode="max-autotune"才勉强持平原生。感觉torch.compile对动态shape和梯度checkpointing的兼容性还是差些,尤其LoRA这种轻量更新,编译器收益容易被通信和反传开销吃掉。你试试把编译范围只包在backbone上,不包整个train_step,可能更实际。另外编译前先跑几十步warmup再计吞吐,不然对比不公平。
我试过同样配置,reduce-overhead对LoRA确实不友好,换mode="max-autotune"再关掉cudagraphs试试。
编译后显存涨是正常的,graph缓存吃内存,小batch下尤其亏,大模型场景不如直接信原生。
同感,reduce-overhead在大模型上经常不如预期,特别是配Deepspeed的时候,编译图跟stage2的通信调度容易打架,显存涨是正常的因为编译会缓存中间buffer。我之前试过把mode换成default或者max-autotune,反而吞吐稳一点,但提升也就10%左右,远没吹的那么神。建议先小规模验证下是不是算子融合跟你的LoRA配置不兼容,另外试试关掉cudagraph只看inductor部分,说不定能找到瓶颈。
Reduce-overhead这模式本来就不是给训练优化的,你换default模式试试,吞吐能涨但显存多占点是常态。
编译开销和显存上涨都正常,LoRA这种小改动真没必要上compile,收益都被deepspeed的通信吃掉了。
说实话reduce-overhead这模式对LoRA这种小参数量训练本来就不友好,编译开销摊不平。我试过7B全参微调用max-autotune倒是有点提升,但也就10%出头,远没有官方吹的那么玄乎。显存变大正常,因为编译会额外生成中间缓冲,你试试把dynamic=True打开可能能缓解一点。不过你要真想提速,不如先检查下dataloader和通信是不是瓶颈,deepspeed stage2下那部分更容易吃性能。
说实话你这情况挺正常的,reduce-overhead模式本来就是为单卡小batch设计的,跟deepspeed stage2叠一起反而会互相抢显存和通信资源。我自己测过7B全参训练,compile只有在batch size够大且不用gradient checkpointing时才有收益,LoRA这种轻量更新场景大部分时间耗在显存搬运上,编译优化不到点子上。建议你试试mode="default"或者干脆max-autotune,但别抱太大期望,这玩意儿对动态shape特别敏感。显存变大确实常见,因为编译器会保留一些中间buffer做图优化,属于正常现象。
我试过类似配置,reduce-overhead在deepspeed下确实容易负优化,换成default模式反而稳一点。
编译那点收益还不如多调两版数据,显存涨倒是正常,图优化要空间换时间。
说实话 reduce-overhead 这个模式在 A100 上对 LoRA 不太友好,它本来是为 CNN 或者大 batch 设计的,小 batch 下 kernel 融合的收益盖不住额外开销,建议你试试 mode="max-autotune" 或者干脆默认模式,另外第一次编译的 warmup 时间确实得忍,后面会好点。
显存变大是正常的,因为编译会生成一些中间缓存和额外的 guard,尤其你叠加了 deepspeed stage2,两边的显存管理可能会有冲突,我这边实测纯 bf16 不加 deepspeed 时编译大概能快 15% 左右,但加了 stage2 后优势基本就没了。
你这情况我更怀疑是 deepspeed 的通讯和 torch.compile 的图优化互相干扰,可以试试只对 model.forward 部分做 compile,把梯度计算和优化器留给 deepspeed 管,或者干脆先用原生跑通 baseline,再单独测编译的增量,别一上来就全堆一起。