最近想上手试试用PyTorch 2.0自带的DDP(Distributed DataParallel)在两张4090上跑一个7B参数的LLaMA风格模型。我原以为开多卡至少能快个1.5倍,结果实际测试下来,单卡跑一个batch大概1.2秒,双卡DDP反而要1.5秒,甚至偶尔还会卡到2秒以上。
用PyTorch2.0训7B模型,DDP比单卡还慢,是我哪里姿势不对吗?
全部回复
共 186 条小模型上DDP开销占比高,7B这规模正常,试试zero1或者干脆开大batch看看。
7B模型才1.2秒一个batch,这吞吐量已经不算小了,DDP的梯度同步开销在这种小batch下会特别扎眼。我怀疑你瓶颈不在计算,而在PCIe带宽或者nccl的通信初始化上,试试把batch再翻倍看看趋势?另外检查下是否设了OMP_NUM_THREADS,有时候默认线程数会导致CPU端数据加载抢资源,反而拖慢GPU。之前我跑3B模型也遇到过类似情况,最后发现是pin_memory没开,加上之后差距才正常。
小模型上DDP开销确实容易盖过收益,7B这个量级建议先查一下数据加载和梯度同步的耗时占比。
4090之间用PCIe互联的话,通信瓶颈太明显了,换NVLink或者干脆上梯度累积试试。
7B模型在两张4090上跑DDP,这个体量确实不太划算,单卡显存够的话通讯开销很容易吃掉收益。你试试把batch size调大点,让每个GPU的计算时间占比上去,或者看看是不是数据加载成了瓶颈。另外PyTorch 2.0的DDP默认用nccl,偶尔卡顿可能是PCIe带宽或者CPU端pin memory的问题,开个async起来也许能缓解。
我上次跑13B也遇到过类似情况,后来发现是梯度同步的等待时间太长了,换成梯度累积加分布式优化器会好很多。你用的什么词表大小和序列长度?小batch下通信占比高是正常的,试试梯度检查点或者干脆用FSDP,说不定单卡显存不够才需要DDP,你这配置感觉有点浪费。
这问题我踩过一模一样的坑,7B模型在双卡上通信开销占比太大了,尤其4090的PCIe带宽跟A100的NVLink没法比,小batch下同步梯度的时间可能比计算还久。你可以试试把batch size翻倍,让每卡算得更久再同步,或者用PyTorch 2.0新出的FSDP,它对大模型分片更友好,通信量和显存占用都低不少。另外检查下是不是默认用了gloo后端,换nccl有时候差别巨大。我上次调完这些,双卡总算比单卡快了1.3倍,虽然还是低于理想值,但至少不亏。
小模型上DDP都这样,通信开销占比太高了,7B才两张卡真不如单卡省心。
7B模型在两张4090上跑,DDP的通信开销占比确实会很明显,尤其是batch size不够大的时候,计算和通信没法重叠。你试试把batch size翻倍,或者开torch.compile,有时候能缓解不少。另外确认下是不是每张卡都吃满了显存,有时候数据加载或者all_reduce的同步点卡住了也会这样。我之前在8卡上跑13B也遇到过类似情况,后来发现是pin_memory没开,数据传输卡顿特别严重。你观察下nvidia-smi和网络流量,如果能看到明显的等待间隙,基本就是通信瓶颈了。
说实话你这情况我遇到过类似的,7B模型在双卡上反而变慢大概率不是DDP本身的问题,而是通信开销把小batch size的收益吃掉了。你单卡1.2秒一个batch的话,算下来每张卡的计算量其实很小,DDP每步都要同步梯度,两张卡之间来回传那几百MB的梯度,延迟比计算还高。建议你先把batch size翻倍试试,让每卡的计算时间拉长到3秒以上,这样通信占比就下来了。另外检查下是不是开了gradient checkpointing,那个玩意儿在DDP下会和all-reduce互相挤占带宽,我之前就被这个坑过。还有个思路是试试torch.compile配合DDP,有时候能把通信和计算重叠起来,但7B模型编译时间挺长的,得有点耐心。
7B模型才1.2秒一个batch,你这通信开销占比太大了,试试梯度累积+更大batch,或者换FSDP看看。
小模型DDP确实容易这样,瓶颈在卡间通信,要不你查下NVLink是不是没跑满,或者干脆用张量并行。
7B模型单卡都塞不满,DDP通信开销全在互相等,试试梯度累积或干脆换张卡跑吧。
这大小上DDP纯属给自己找罪受,先看下nvidia-smi是不是都在满载干活。
这情况我遇到过,当时差点怀疑人生。你先把gradient checkpoint和混合精度关了试试,7B模型在4090上本身显存就吃紧,DDP的all-reduce通信如果跟计算重叠得不好,反而会变成瓶颈。另外你确认下是不是每个卡单独算loss再同步梯度,如果用了梯度累积,DDP的bucket大小没调好,通信频率过高也会拖慢速度。
还有一个常见的坑,就是DataLoader的num_workers设置,单卡可能默认够用,但双卡时如果每个进程都加载重复数据,CPU会成为瓶颈,特别是tokenize复杂的话。建议把dataset提前预处理成二进制格式,或者用DistributedSampler确保每个rank只读自己那部分,不然IO等待时间全算在step里了。
另外你这1.2秒是纯forward+backward,还是包含了优化器更新?如果包含的话,建议单独测一下通信时间,用torch.distributed.barrier()包住一个假的all-reduce,看看是不是网卡或者PCIe带宽的问题。两张4090如果走的是PCIe 4.0 x16,其实带宽还行,但有些主板插第二槽只有x8,那通信开销会明显放大。
我自己的经验是,小batch下DDP收益几乎为零,甚至负优化,得把batch size调到单卡能承受的极限,比如把seq len拉到2048以上,让计算占比超过通信,这样多卡才有意义。你试试把global batch保持一样,单卡跑2步对比双卡跑4步,看总吞吐量是不是真的提升了。
还有个偏门但很实际的可能:你是不是用了PyTorch 2.0的compile?DDP和torch.compile一起用有时候会触发重复的图编译,导致每个step前有额外的同步开销。可以先关掉compile再测,或者试试用DDP包住之后再compile,顺序很重要。
最后问下,你观察到的卡顿是不是在第一个step之后?如果是,大概率是cudnn benchmark或CUDA context初始化在多个进程里串行执行了。可以试试设置环境变量CUDA_LAUNCH_BLOCKING=0,或者用torch.cuda.set_per_process_memory_fraction稍微限制下每卡显存,减少动态分配冲突。
这情况我遇到过,7B模型在双卡上如果batch size没跟着翻倍,DDP的梯度同步开销反而会吃掉大部分收益,尤其4090这种带宽卡。你可以试试把单卡batch翻倍再对比,或者查下是不是all_reduce通信没走NVLink,跑在PCIe上延迟会高很多。另外PyTorch 2.0的compile模式有时候对DDP不友好,关掉再测测看,说不定有惊喜。
7B模型用DDP在两张卡上反而变慢,大概率是卡间通信开销把计算收益给抵消了。4090的PCIe带宽本来就有限,小batch下梯度同步时间占比会特别明显,你可以试试把batch size翻倍或者用梯度累积,让单卡计算时间拉长。另外PyTorch 2.0的编译模式跟DDP有时候会有兼容性问题,关掉compile或者换用FSDP可能反而更快。我之前跑6B模型也遇到过类似情况,后来发现是数据加载成了瓶颈,两张卡抢同一份数据,改个独立dataloader就正常了。你监控一下GPU利用率,如果不到80%基本就是同步等太久。
7B模型在两张4090上跑,这个规模本身就卡在通信瓶颈上了,单卡显存够用的话DDP只会徒增开销。你可以看看是不是没设好梯度累积或者bucket容量太小,另外试试把nccl的ring算法换成tree,有时候小规模多机比单机多卡还吃配置。我之前调4卡的时候也遇到过类似情况,最后是把batch size翻倍才勉强追平单卡速度。
7B模型在4090上单卡推理/训练其实已经接近显存带宽瓶颈了,DDP的梯度同步开销反而成了大头,尤其batch小的时候。我之前试过用gradient checkpointing把单卡batch压到极限再开DDP,提升也就勉强10%,后来干脆转用zero-1把优化器状态切片,才稍微好点。你检查过NVLink或者PCIe带宽吗?4090之间如果是走CPU跨socket通信,那延迟高得吓人,建议先用nccl-tests测下实际吞吐。另外PyTorch2.0的compile对DDP有额外编译开销,第一个batch慢很正常,但后面如果还慢就得看是不是数据加载成了瓶颈。
7B模型塞进两张4090,DDP反而更慢这个现象其实挺典型的,我怀疑你大概率是撞上了all-reduce的通信瓶颈。单卡1.2秒一个batch,说明数据加载和计算本身没毛病,但DDP在每个step结束都要同步梯度,7B模型哪怕只训得动很小batch,梯度张量也有几百MB,PCIe带宽在这时候就是硬伤。你试试把batch size再调大点,让单卡计算时间拉长,通信开销占比自然就降下来了,或者换个思路,用梯度累积模拟大batch,比直接上DDP可能更稳。另外PyTorch 2.0的compile模式跟DDP组合起来有时候会触发奇怪的图优化问题,我见过有人关掉compile反而正常了。还有个小细节,你是不是忘了设环境变量NCCL_P2P_DISABLE=1?两张卡如果是通过PCIe而非NVLink互联,这个不关掉会有额外的握手开销。最后想确认下,你测速的时候是不是把warmup步数跳过了?DDP前几个step会做带宽探测和缓冲区初始化,经常慢得离谱,多跑二十步再计时才准。如果这些都不行,可能就得接受现实了,2卡4090跑7B本来就是勉强够显存,性能不增反降不算稀奇。
我之前也踩过类似的坑,7B这种规模用DDP在两张卡上,通信开销占比太高了,尤其4090这种卡互联带宽有限,单卡算力又强,小batch下反而容易负优化。你可以试试把batch size调大,或者用梯度累积,让计算和通信重叠起来看看。另外检查一下是不是没用torch.compile,或者数据加载成了瓶颈,有时候DataLoader的num_workers没调够也会拖后腿。实在不行干脆换FSDP,对这种模型尺寸可能更友好。
7B模型才两张卡,通信开销全在权重同步上了,正常,换张H100或者上张4090单卡反而更划算。
试试梯度累积加大batch size,把单卡吃满再说,DDP那点收益真不够看。
这情况太典型了,7B模型在双卡上跑DDP,通信开销很容易吃掉计算收益。你先看看是不是没开nccl的p2p优化,或者数据加载环节成了瓶颈,毕竟4090的PCIe带宽扛不住频繁梯度同步。我之前调小batch size再加大梯度累积,反而快了不少,你可以试试把gradient_as_bucket_view打开,有时候能省不少内存拷贝时间。另外确认下是不是每个step都做了torch.cuda.synchronize,那玩意儿在DDP里会拖慢节奏。要是还不行,干脆上ZeroRedundancyOptimizer或者FSDP,对小规模多卡更友好。
7B模型用DDP跑双卡,通信开销占比确实是个坎儿,尤其batch size不大的时候,梯度同步那点时间可能比计算还扎眼。你试试把batch size翻倍,或者用gradient accumulation把计算量堆上去,看看能不能把通信成本摊薄。另外PyTorch 2.0的compile模式和DDP的配合有时候会搞出莫名奇妙的同步延迟,可以先关掉compile纯跑DDP对比下。还有个小细节,4090的PCIe带宽其实挺吃紧的,双卡互联如果是走主板而不是NVLink,性能损耗更明显,这个也得考虑进去。