最近在折腾用 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 条这问题我太有感触了,之前拿Llama 2 13B试过同样的配置,torch.compile在deepspeed stage2下不仅没加速,反而把显存吃上去快2G,后来查了下感觉是编译生成的中间张量跟deepspeed的显存分片逻辑有冲突。你换mode="max-autotune"试试,reduce-overhead那个模式本来就更适合小batch的推理,训练场景下它为了减少CPU启动开销做的那些优化反而会限制计算图的重排空间。另外你用的LoRA本身可训练参数就少,计算瓶颈基本在attention的矩阵乘上,这恰好是cuBLAS已经优化得很极致的地方,编译能榨取的剩余价值确实有限。我自己在纯dense训练(不加载lora)时测过,7B模型配gradient checkpointing,compile大概能拿到15%左右的吞吐提升,但前提是得把deepspeed关掉只开bf16,两者同时用总是互相拖后腿。显存变大是正常的,因为编译会保留更多中间变量做算子融合,你试试把max-autotune和memory_format=channels_last一起开,有时候能缓解。说到底这玩意儿对A100这种卡来说更像锦上添花,真正瓶颈在数据加载和通讯的话,折腾编译纯属浪费时间。
说实话reduce-overhead这个mode在A100上确实容易反向优化,它对小算子融合收益大,但大模型训练瓶颈在通信和显存带宽上,编译反而可能增加中间张量的开销。我试过llama系列,用inductor默认模式或者max-autotune配合deepspeed反而能稳一点,但也就快5%-10%,没吹得那么神。显存涨是正常的,因为编译器会做算子缓存和重排,尤其你开了stage2,本来就有activation checkpointing的交互,建议先关掉deepspeed单测一下纯编译的吞吐对比,排除配置干扰。另外第一次编译慢是正常的,但如果你每次改超参都重新编译,那确实不如直接跳过,这玩意儿更适合固定shape的推理或小规模反复迭代场景。
说实话你这个配置我太有同感了,之前我在A100上跑LLaMA微调也踩过一模一样的坑。torch.compile在CV或者小模型上确实能吃到甜头,但一上大模型加Deepspeed,它跟ZeRO的通信和内存管理经常打架,尤其是reduce-overhead模式,那个CUDA graph的捕获开销在动态shape下会直接拖垮训练。我自己实测下来,LoRA场景下compile能保住正收益的往往是batch size特别大、计算密集比重高的时候,你单卡跑7B本来显存就紧,编译后多出来的缓存和graph内存很容易把吞吐顶下来,慢个10%-20%都算正常。而且你用的bf16,原生算子已经挺优化了,compile能压榨的算子融合空间有限,不像FP32那样提升明显。我后来干脆只在推理阶段开compile,训练就用原生加flash attention,反而省心。你要是真想试,可以试试mode="max-autotune"加fullgraph=True,但先把deepspeed换成zero1或者干脆不用,排除一下通信干扰。显存开销变大确实正常,尤其第一次编译会存不少中间buffer,跑几个step之后有些能回收,但别指望它省显存。
同感,reduce-overhead在A100上经常负优化,试试mode="max-autotune"配合fullgraph=True,提升明显些。
同感,reduce-overhead在loRA这种小参数量更新场景下确实容易负优化,编译开销都摊在显存拷贝和kernel调度上了。我之前试过dynamo+inductor配fsdp,只有batch size拉满到接近OOM时才有10%出头提升,而且显存涨是正常的,编译会缓存activation和一些中间tensor。你试试mode="max-autotune"加fullgraph=True,然后给deepspeed的zero阶段关掉offload,可能反而有惊喜。至于玄学调优,这玩意儿真得看具体模型结构和卡数,目前社区也没个统一结论,别太信宣传。
同款配置踩过坑,torch.compile在LoRA这种小参数量更新场景收益确实有限,尤其deepspeed的stage2本来就有额外通信开销,编译优化容易被掩盖。建议试试把mode换成max-autotune,然后关掉cudagraphs,我这边吞吐反而涨了15%左右。显存变大正常,编译会保留一些中间buffer做算子融合,7B模型多占2-3G可以接受。另外第一次编译那几分钟可以忍,后续缓存在磁盘上会快很多。
小模型上compile收益明显,7B这规模纯属给编译器和显存添堵,换reduce-overhead纯亏。
试过把deepspeed关掉只开bf16吗?我这边A100上反而能提10%左右,显存开销大正常,图优化要额外buffer。
说实话你这个情况我太熟了,之前我拿7B模型试过一模一样的组合,reduce-overhead在短序列上确实会倒挂,但把序列长度拉到4k以上,编译优势才慢慢出来。大模型场景下瓶颈多半在通信和显存带宽,torch.compile优化的是算子融合和kernel launch,这部分占比被deepspeed的通信时间稀释后,收益自然不明显。而且你开stage2再加bf16,本来就已经挺接近硬件极限了,编译能挤出的油水真不多。至于显存变高,那是正常的,编译会生成额外buffer和中间表示,尤其reduce-overhead模式会预分配内存,你拿nvidia-smi看可能多了几百M到1G,不算bug。我建议你先别开编译,把gradient checkpointing和flash attention弄上,吞吐可能比折腾torch.compile实在得多。另外如果你真想测,可以试试mode="max-autotune"配dynamic=False,固定shape后编译时长会降不少,但首次跑还是得等个几分钟。最后提醒一句,如果你用deepspeed的话,检查下zero阶段和编译是否冲突,有些版本下offload跟torch.compile会打架,导致回退到eager模式,那就等于白等。
说实话reduce-overhead模式在A100上对LoRA这种小batch场景经常是负优化,它主要针对的是大batch训练时的CPU bound问题。你deepspeed stage2本身已经做了梯度分片,和编译的算子融合会有一定冲突,建议试试mode="default"或者关掉deepspeed纯用fsdp看看。显存变大也正常,编译会保留一些中间buffer做图优化,但如果你发现多出来的显存超过10%就要检查是不是图捕获把动态shape卡住了。我自己的经验是,7B模型在单卡上编译收益确实不明显,等上多卡或者更大模型时才有感知。
说实话你这情况我还真遇到过,当时也是单卡A100跑LLaMA微调,torch.compile开reduce-overhead反而掉点。后来我排查了一圈,发现跟deepspeed的stage2耦合时,编译图经常被切碎,尤其是LoRA那种只改部分参数的场景,graph break特别多,等于白做了 fusion。你试试把mode换成default或者max-autotune,前者虽然保守点但至少不会负优化,后者预热时间长但跑起来确实能好一些。
另外显存开销变大是正常的,因为编译会生成额外的中间缓存和CUDA context,这跟你用不用deepspeed关系不大,主要是capture graph会占一部分显存。我自己的经验是,小batch size下编译优势完全发挥不出来,你得把batch撑到接近显存上限,这时候内核融合的收益才能盖过调度开销。还有一点,如果你用了deepspeed的offload,那基本告别编译加速了,因为参数在CPU和GPU之间来回搬,图优化全被打断。
我后来干脆放弃了torch.compile,改用手动把attention和MLP的kernel用flash attention 2 + fused AdamW替代,反而稳定提升了15%左右。说到底,这玩意儿对小模型和静态shape友好,大模型训练场景里变量太多,真不如老老实实把数据加载和通信压一压。你要是真想试,建议先关掉deepspeed单独跑个纯模型训练对比一次,再逐项加回来,不然根本分不清是哪一层的锅。
说实话你这情况我太熟了,之前拿7B跑SFT也踩过一模一样的坑。torch.compile那30%-50%的加速水分很大,基本是拿CNN或者小模型刷出来的benchmark,到了大模型场景,尤其是带上deepspeed后,通信量和显存开销会直接吃掉编译带来的收益。你用的reduce-overhead模式本来就激进,它为了减少Python开销会额外搞CUDA graph,显存涨个几个G太正常了,尤其在bf16下更明显。我个人实测下来,单卡A100跑LoRA,torch.compile最多也就快个5%-10%,有时候还负优化,特别是当你的batch size比较小、训练步数又不多的时候,编译那几分钟成本根本回不来。建议你直接关掉编译,把精力放在gradient checkpointing和attention实现上,或者试试flash attention 2,那个收益反而稳定。另外deepspeed stage2和torch.compile的兼容性一直有点问题,你可以看一眼日志里有没有warmup的警告,有时候编译器根本没生效只是在硬跑。如果你非要试,建议用mode="default"或者"max-autotune",但别在短实验上浪费电费了。
reduce-overhead 在小 batch 下确实容易负优化,你试试 mode="max-autotune" 配合 fullgraph=true,吞吐提升会明显些。
说实话跟你体验差不多,我拿7B模型试过几次,torch.compile在纯训练循环里那点收益基本被编译开销和显存峰值吃掉了,尤其配合deepspeed的stage2,图优化经常和offload逻辑打架。你可以试试只对单卡、不用deepspeed的场景开compile,或者把模式换成default而不是reduce-overhead,后者在某些kernel上反而会更慢。显存变大挺正常的,因为编译会保留一些中间buffer,我建议直接看step time而不是盯着显存,如果没明显下降就果断关掉,别在工程技巧上死磕。
说实话我最近也踩过这个坑,跟你情况几乎一模一样。torch.compile对静态shape的CNN或Transformer encoder效果确实明显,但到了Llama这种带KV cache的decoder架构上,动态shape一多,编译器反而很难做图优化,尤其你开了reduce-overhead,它内部会做CUDA graph捕获,但deepspeed stage2的partitioned optimizer和bf16的autocast叠加进来,很容易让捕获失败然后回退到eager模式,那个等待时间其实就是白等。显存变大这个我怀疑是编译时保存了额外的中间缓存或者activation checkpointing的图结构,我试过把mode换成default或max-autotune,反而比reduce-overhead更稳,吞吐能提升10%左右,但远没到30%。另外你可以试试把batch size调大一点,编译的kernel融合收益在小batch下会被启动开销吃掉,我这边256序列长度下batch到32以上才看到正向收益。还有个小技巧,先warmup跑几个step再计时,不然第一次迭代的编译时间会污染整个统计。你要是用peft的话,记得把gradient_checkpointing关掉试一次,那个跟torch.compile的recomputate逻辑有时会冲突。总之这玩意儿不是无脑开,得看具体模型和训练配置,我最后是干脆只对linear层手动写了个kernel fusion,比torch.compile稳定。
你这配置我试过,reduce-overhead对LoRA反而容易负优化,换default模式或者关掉编译用deepspeed本身可能更稳。
编译显存变大正常,主要是图优化和缓存开销,小batch下尤其明显。
同款配置踩过坑,reduce-overhead在小batch下反而有额外开销,编译启动那几分钟基本白等。后来我试了mode="max-autotune"配合static_shape=True,LoRA吞吐大概能提15%左右,但显存确实会多吃几个G,估计是CUDA graph缓存占的。你这情况大概率是deepspeed的stage2和torch.compile的inductor有兼容问题,建议先关掉deepspeed单独跑一版对比下。另外编译前把vocab_size对齐到64的倍数有时也会有惊喜,你可以试试。
说实话reduce-overhead这个模式在大模型上确实容易翻车,它主要优化的是小算子启动开销,但Llama这种场景算子已经够大了,反而吃了编译期显存翻倍的亏。我试过用inductor默认模式配deepspeed,吞吐能提个10%左右,但前提是把max-autotune关掉,不然光搜配置就够你喝一壶。你试试把编译范围限定在attention那块,或者干脆用torch.compile的fullgraph=False,微调场景下别指望白嫖太多。另外显存变大是正常的,编译会保留中间buffer,7B模型建议把activation checkpointing开着。
编译开销在大模型上确实容易吃不满,尤其LoRA这种轻量场景,建议试试mode="max-autotune" + 关掉cudagraphs再看。
显存变大正常,reduce-overhead会缓存额外buffer,小batch下尤其明显,不如直接换unsloth省心。
我跟你遇到的情况挺像的,单卡A100跑LoRA的时候compile反而拖后腿,后来发现reduce-overhead那个模式对大batch不友好,显存开销大是因为它做了算子融合缓存,属于正常现象。建议你试试mode=“default”或“max-autotune”,另外deepspeed stage2本身会改图,和compile有冲突概率,我们最后直接关掉deepspeed用原生zero2,吞吐反而上来了。编译提速一般要模型够大且训练步数够长才能摊平开销,7B这种规模得看具体算子吃不吃得满,别迷信官方博客的数字。
说实话reduce-overhead在A100上对LoRA这种小batch场景确实容易负优化,编译本身的开销摊不平。我试过在7B全参数微调时用default模式配deepspeed,吞吐大概就提了10%左右,远没宣传的夸张,显存涨倒是真的,因为编译会生成额外buffer。你不如把精力放在gradient checkpointing和flash attention上,那个收益更稳,编译这玩意儿适合大batch跑CNN或GPT预训练那种场景。