尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

多卡GPU训练扩展效率:为什么双卡跑不满两倍速度?

发布时间:2026/9/29 18:45:24

资讯中心
01
ARTICLE

多卡GPU训练扩展效率:为什么双卡跑不满两倍速度?

多卡GPU训练扩展效率:为什么双卡跑不满两倍速度?
说实话第一次把第二张 GPU 插进机器、满怀期待地跑起 LLM 训练然后看到训练时间只缩短了三分之一的时候我第一反应是怀疑自己买到了假卡。群里一问才发现这不是我一个人踩过的坑几乎每个从单卡切到多卡的人都会经历这个“双卡怎么没有快一倍”的失落瞬间。等后来把扩展效率这件事真正搞清楚我才明白问题往往不是出在硬件上而是出在测量方法和并行机制的理解上。这篇文章就围绕这个核心问题展开为什么两张 GPU 的理论峰值翻倍了实际训练速度却到不了两倍以及更重要的——用什么方法、什么指标才能正确测量多卡扩展效率而不是靠“训练总时长除以二的期待”去判断成败。内容会覆盖扩展效率计算公式、同步训练与 AllReduce 通信机制、利用率曲线的解读方法、以及我实测中总结出来的优化手段。不论你是刚入手双卡工作站还是在调多机多卡训练框架这篇都能给你一个可落地的测量和分析思路。1. 双卡只快 1.6 倍先看清楚扩展效率的定义和计算很多人的第一个误区是把“加速比”和“扩展效率”混为一谈。双卡跑同一个训练任务总时长从 10 小时变成 6 小时通常会听到两种说法一种叫“加速比 1.67 倍”另一种叫“扩展效率 83.3%”。这两个数字是一样的东西但表述角度完全不同。加速比关心的是“快了多少钱”扩展效率关心的是“多花的钱值不值”。1.1 扩展效率的计算公式与基准定义教科书式的定义很简单扩展效率 单卡基准耗时 /多卡耗时 × 卡数。假设单卡跑一个固定训练任务需要 100 分钟双卡跑同样的任务需要 55 分钟那么加速比是 100/55 ≈ 1.818扩展效率是 1.818/2 ≈ 90.9%。这个数字的含义是每增加一张卡它带来的实际收益相当于它理论算力的九成左右。剩下的 9.1%就是并行化过程中损失掉的性能。这里要特别强调“固定训练任务”这几个字因为它决定了测量结果的公平性。如果你只是把总 batch size 保持不变从单卡切到双卡那么每张卡的 batch size 会减半模型更新的次数完全一样。这种情况下测出来的加速比代表的是“同样迭代次数下多卡带来的时间节省”它受通信开销影响非常大。另一种做法是保持每张卡的 batch size 不变让全局 batch size 翻倍那么模型看到的样本数在同样步数内翻倍了。这种测量方式更接近真实训练场景因为大家买多卡就是为了在更短的时间内吃下更多的数据。两种定义没有绝对对错但测量前必须明确自己测的是哪一种否则拿到结果后根本无法横向比较。1.2 为什么固定全局 batch 和固定 per-GPU batch 会得到不同的扩展效率我先说结论固定全局 batch 测出来的扩展效率通常会更高但这个更高是“虚”的。原因在于保持全局 batch 不变时每一步的计算量本来就变小了每张卡只处理原来一半的样本而梯度通信量在整个迭代时间里的占比会被计算量的缩小放大。看起来效率数字还不错但实际上这个测试既没有反映真实训练的吞吐量也没有给你任何可以指导调优的信息。固定 per-GPU batch 则是更接近工业界标准的做法。比如单卡时你在一个 step 里吃 64 条样本双卡时就变成每卡 64 条、全局 128 条。训练同样数量的 epoch双卡可以少跑一半的 step这才是用户真实付出的训练时间减少量。这时候扩展效率如果达到 85%-95%已经是非常健康的水平如果掉到 70% 以下就说明通信、数据加载或者负载均衡里面一定有一环出了问题。我用一个表格把两种测试方案的差异整理出来测量方案单卡 batch双卡 batch双卡步数关注指标更适合什么场景固定全局 batch6432/卡和单卡相同每步耗时对比算法改动、单卡代码逻辑迁移固定 per-GPU batch6464/卡单卡的一半每秒处理样本数真实训练效率、硬件资源评估如果你只是想确认“代码从单卡改成 DDP 后还能不能正常跑”用第一种就够了但如果你想评估“再多买两张卡值不值得”请务必用第二种。我自己的习惯是两个都测先用固定全局 batch 快速确认功能正确性再用固定 per-GPU batch 跑一个较长时间的稳定测试来评估扩展效率。2. “快一倍”为什么这么难同步通信与 Amdahl 效应搞清楚了怎么算效率接下来要回答的是那个核心的“为什么”。两张卡的理论算力就是单卡的两倍但任何深度学习分布式训练都不可能达到 2.0 倍的加速比。这不是硬件厂商的猫腻而是并行计算里两条基本定律在起作用一条是 Amdahl 定律另一条是同步训练中 AllReduce 通信的硬性开销。2.1 反向传播之后必须做的 AllReducePyTorch 里大家最常用的多卡训练方式是 DistributedDataParallelDDP。它的核心逻辑是每个 GPU 上放一份完整的模型副本各自用自己的数据前向传播和反向传播算出一份本地梯度。但问题来了——如果每张卡拿着不同的梯度各自更新参数训练就变成各练各的最终得到的模型权重会不一致这等效于把 batch size 拆散之后又没有一个统一的参数更新规则训练基本会崩。所以 DDP 在每个 step 的反向传播结束后会执行一个 AllReduce 集合通信操作把各张卡上的梯度拿过来做平均再把平均后的梯度广播回每一张卡确保所有卡接下来用完全相同的梯度去更新参数。这个过程需要传输的数据量和你模型的参数量成正比。模型越大通信的字节数越大通信耗时也就越长。这也解释了为什么大模型训练时多卡扩展效率往往不如小模型——通信开销的绝对值变大了而计算时间的增长未必能完全掩盖它。2.2 通信时间为什么随卡数增长最小通信量公式AllReduce 有一个理论上的最小通信量公式。假设模型参数量为 M 字节GPU 数量为 P那么每步训练中AllReduce 需要传输的数据量最少为 2×(P−1)/P × M 字节。P2 时这个值是 1×M也就是双卡每步至少要在 GPU 之间搬运一个完整模型的字节数P4 时是 1.5×MP8 时是 1.75×M。卡越多通信总量越大但增幅在放缓。我把这个公式拆开解释一下所有卡先把自己算好的梯度分片发给其他卡然后从其他卡接收合并后的梯度这是一个“先发后收”的过程所以会有 2 倍的系数。而 (P−1)/P 反映的是分片减少重复传输的效果。很多初学者以为卡越多通信越快实际上恰恰相反——通信总量随卡数单调上升所以扩展效率几乎不可能随卡数增加而变好。用一个具体的例子算一下假设你的模型有 70 亿参数用 FP16 混合精度训练每个参数占 2 字节模型总大小就是 14GB。双卡时每步 AllReduce 至少要在两张卡之间搬运 14GB 数据。如果在 PCIe 4.0 x16 的链路下实际带宽大概 20GB/s 上下那么光是通信就要 0.7 秒以上。而同样规模模型的单卡训练一个 step 的计算时间可能也就 5-10 秒。通信占了计算时间的 7%-10%扩展效率自然到不了 95% 以上。如果换到 NVLink 环境带宽接近 PCIe 的 5-10 倍通信开销占比会明显下降但绝不会消失。2.3 同步屏障与负载均衡最慢的那张卡拖住所有人DDP 是同步训练模式这意味着每张卡算完本地梯度后必须等待所有其他卡的梯度都到达才能统一更新参数。这个“等待”就是同步屏障barrier。你两张卡中只要有一张因为散热降频、数据加载偏慢、或者其他进程抢占资源而变慢整步训练的时间就会被拖到和慢卡一样。这有点像两列火车并行前进以较慢的那列为准。两张卡性能完全相同的情况在真实环境里很少见尤其是消费级显卡相邻两张卡的动态频率、显存温度都不完全一致。多跑几万个 step 之后慢卡积攒的延迟会让总耗时比理想值多出几个百分点。如果你还开了其他程序占显存或占 CPU这种负载不均衡会被进一步放大。测量扩展效率时要确保测试环境里没有其他 GPU 任务在跑同时最好固定 GPU 频率避免动态频率波动污染数据。3. 测量工具的选型与正确测量流程知道了理论和公式之后有一个残酷的事实很多人在评估多卡效率时用的是训练日志里打印的“total training time”然后草率地除以卡数得到一个让人失望的数字。这个数字能不能说明问题能但它只能告诉你最终结果无法告诉你瓶颈在哪里。正确的测量思路应该是分层进行先看总体耗时算扩展效率再用工具定位每一步的时间构成最后深入到利用率曲线判断瓶颈类型。3.1 用 nvidia-smi 看“毛估估”利用率数字的真实含义最容易上手的工具是 nvidia-smi。运行 nvidia-smi -l 1 就能每隔一秒打印一次 GPU 状态里面有一个“Utilization”字段。但很多人在这一步就掉进坑里了他们把 Utilization 当作“计算单元有多忙”的指标实际上这个数字是采样周期内 GPU 上某个引擎比如图形引擎或复制引擎处于活跃状态的时间百分比。它并不等于 SM流式多处理器的忙碌程度也不等于算力被充分利用的程度。举个例子一张卡如果在拼命做数据搬运比如把数据从显存拷到显存、或者做设备到设备的拷贝它的 Utilization 可能显示 100%但 SM 上真正跑的计算 kernel 可能只有 50% 的占用率。反过来一个计算密集但单个 kernel 很短的任务因为采样窗口粒度太粗利用率数字反而可能很低。所以 nvidia-smi 的 Utilization 只适合用来做粗粒度观察看两张卡是不是都在动、有没有一张长期闲置。要判断计算效率必须进入下一层工具。3.2 用日志记录每步耗时稳定窗口内的统计采样我用来判断扩展效率的主力数据是训练脚本里打印的每步耗时seconds per iteration。具体做法是每 10 个 step 输出一次平均 step 时间并且过滤掉前几十个 step 的预热数据。为什么要过滤因为 PyTorch 启动、CUDA context 初始化、NCCL 通信域建立、数据加载器预热这些都会让最开始的几十个 step 明显变慢。如果把预热期数据算进去扩展效率会被低估。测量时我会让训练脚本在固定 per-GPU batch 下跑 200-300 个 step取第 50 步到第 250 步的平均值作为该配置下的稳定 step 时间。然后分别测单卡和双卡算出每秒钟处理的样本数throughput)最后用公式算出扩展效率。这个流程的重复性很好并且能把“训练总时长”里很多无关变量比如 checkpoint 保存、评估循环、日志写入的影响隔离开。如果你只想快速看个趋势也可以跑 100 步就够但至少要在日志里确认 step 时间已经进入平台期。3.3 记录稳定窗口利用率的 pynvml 小工具除了训练日志我会用 pynvml 写一个小脚本记录一段时间内每张卡的利用率和显存占用。nvidia-smi 是手动看pynvml 是自动采样适合在跑长训练时同时记录之后再用脚本分析。依赖安装很简单pip install nvidia-ml-py。下面这段代码会每隔一秒打印当前所有 GPU 的利用率和已用显存import pynvml import time pynvml.nvmlInit() device_count pynvml.nvmlDeviceGetCount() handles [ pynvml.nvmlDeviceGetHandleByIndex(i) for i in range(device_count) ] for _ in range(60): line [] for i, handle in enumerate(handles): util pynvml.nvmlDeviceGetUtilizationRates(handle) mem pynvml.nvmlDeviceGetMemoryInfo(handle) line.append(fGPU{i}: util{util.gpu}%, mem{mem.used // (1 20)}MB) print( | .join(line)) time.sleep(1)运行方式很简单终端 A 跑训练脚本终端 B 跑这个采样脚本。等训练进入稳定期后收集 60 秒左右的数据。回头分析时重点关注两个信息第一两张卡的利用率是否都维持在高位且接近相等第二显存占用是否也对称。如果出现“一张 95%、另一张 60%”的长期不对称基本可以断定负载分配出了问题。3.4 用 nsys 看到通信时间占比进阶分析方法nvidia-smi 和日志能告诉你“扩展效率不理想”但很难告诉你“到底慢在哪里”。这时候需要用 Nsight Systemsnsys这类 profiler 工具把每一个 step 内部的时间构成拆开。nsys 的安装和 Nsight Compute 是一套体系装好之后可以直接采集训练进程nsys profile -o perf_report --force-overwrite true --tracecuda,nvtx python train.py --epochs 1如果训练脚本里用了 torch.profiler 或者 NVTX rangensys 能按照你标注的阶段把时间归类。即使没有任何标注它也会在时间线上展示每个 CUDA kernel 的类型和耗时。我通常会在脚本里用 PyTorch 自带的 torch.profiler 把 forward、backward、optimizer 三个阶段包起来这样能直接看 backward 阶段里有多少时间是 NCCL 的 allreduce kernel 在占用。用 profiler 时最值得关注的一个指标是通信 kernel 的总耗时占一个 step 总耗时的比例。如果这个比例超过 20%意味着通信已经成为主要瓶颈你后续的优化方向就应该是减少通信频率、缩小通信数据量或者让通信和计算重叠。如果比例只有 3%-5%那么扩展效率损失更多来自负载不均衡或数据加载盲目去调 NCCL 环境变量就是白费力气。4. 看懂利用率曲线判断瓶颈落在哪一环有了一批稳定数据之后接下来的工作是“读曲线”。GPU 任务在时间线上的利用率表现往往会直接告诉你问题出在哪个环节。我把训练中常见的异常利用率曲线分为三类每一类对应的瓶颈不同解决手段也完全不一样。4.1 三种特征曲线对应三种问题第一种是“锯齿波”或“一高一低”曲线两张卡的利用率交替上下跳动或者长期存在一张高一张低。这种形态通常是负载不均衡的表现。数据加载阶段如果每个 worker 随机取样本的时候没有全局均匀切分或者 CPU 预处理时间不一致就会导致每张卡的 step 完成时间有偏差。同步模式下快卡每步都要等慢卡反映到利用率上就是等待期间利用率掉下去。解决思路是检查 DataLoader 的 num_workers 设置、确保跨卡数据顺序均匀、以及在训练启动前固定随机种子让两张卡拿到完全对称的数据流。第二种是“双高但加速比上不去”曲线两张卡利用率都在 90% 以上看起来都很忙但每步耗时并没有随卡数翻倍而减半。这种情况极大概率是通信时间没有和计算时间重叠。DDP 的默认实现中AllReduce 通信是在 backward 的梯度就绪后逐步进行的PyTorch 会做梯度分桶bucket来让通信和计算部分重叠但重叠的效率和梯度产生的时间分布密切相关。如果反向传播的梯度计算集中在最后几层通信就会被压缩在很小的时间窗口内形成“计算一段通信一段”的锯齿。这时用 nsys 看 kernel 时间线会非常直观。第三种是“频繁跳水”曲线两卡利用率在高位运行但每隔几十步就突然掉到很低然后又恢复。这通常是周期性事件造成的常见来源有checkpoint 保存、评估集推理、日志写入、以及数据加载到了一个新的 epoch 起点需要重新 shuffle。如果这些操作没处理好它们会吃掉不少吞吐量。比如 checkpoint 保存时如果没有异步执行训练循环会阻塞在磁盘 IO 上整个 GPU 一起等着写盘完成。4.2 关键指标区分SM Activity / SM Occupancy / Memory Throughput很多读过 NVIDIA 文档的同学会被一堆指标弄晕。实测中我最常用的几个 profiler 指标是SM Activity流式多处理器活跃度、SM OccupancySM 上活动线程块占用率、Memory Throughput显存带宽利用率。这三者代表完全不同的东西。SM Activity 反映的是 SM 实际执行指令的忙碌程度接近于很多人直觉里的“GPU 算力利用率”。SM Occupancy 反映的是理论上最多能驻留多少个线程块、实际驻留了多少个它偏低时表示并发线程不够kernel 启动密度不足常见于小 batch size 或 kernel 太琐碎的场景。Memory Throughput 高而 SM Activity 低说明任务是被显存带宽限制的比如很多 element-wise 操作和数据搬运型 kernel。看曲线的时候把这三列放在一起看基本能定位你的训练是被计算限制、访存限制、还是延迟限制。分析完这些再去对比多卡与单卡的差异通信开销的占比自然就浮出来了。4.3 拓扑层面的隐性浪费P2P 不可用时你根本看不见它还有一个非常隐蔽的浪费通常在测量扩展效率时会让你觉得“什么优化都做了还是差一口气”。这个坑就是 GPU 之间的点对点通信P2O没有被启用。双卡环境下如果两张卡之间没有 NVLink 桥接或者 NCCL 检测到 P2O 不可用它会退回到通过 CPU 内存中转的通信路径即先把数据从 GPU 拷到主机内存再拷到另一张卡。这条路径的带宽比 GPU 直连低一个数量级你的扩展效率会被瞬间拖到 50% 以下。检查方法很简单跑一个小脚本调用 torch.distributed打印 NCCL 的通信方式。或者直接设置环境变量 NCCL_DEBUGINFO观察日志里是否有直接用 GPU 显存地址通信的记录。如果发现走了共享内存或者主机内存路径先检查主板 PCIe 拓扑、NVLink 桥接器、驱动版本以及 BIOS 里是否开启了对等映射相关的选项。我在一台工作站的实测中发现重装驱动后 P2O 默认行为变化过就是一个活生生的例子——扩展效率从 88% 掉到 65%折腾半天最后发现是 NCCL 环境变量被某次安装脚本污染了。5. 把第二张卡的性能“挤”出来主要优化方向知道了瓶颈接下来就是动手优化。这一节涉及的手段我都亲自在真实训练任务里验证过按收益从大到小排列。注意优化之前先记住一个铁律——每次只改一个变量改完重新测扩展效率。同时改三个变量跑慢了都不知道怪谁。5.1 通信时间“藏”到计算里AllReduce 与反向传播的重叠DDP 中最关键的优化机制是让梯度通信和反向传播计算尽可能重叠。PyTorch 的实现方式是把模型参数按字节数分成若干桶bucket每个桶的梯度算好后立即对这个桶做 AllReduce而不是干等所有梯度都算完再统一通信。这样整体通信时间就会被“埋”在反向传播的计算过程中而不是暴露为一段独立的等待。实测里我发现影响重叠效果的因素有两个一是 bucket 的设置二是梯度产生的顺序。如果你的模型太大通信数据量远远大于小桶能覆盖的范围重叠收益会递减。PyTorch 默认的 bucket_size 是 25MB通常不用改但如果你用很小的模型比如百 MB 级可以试试把 bucket_cap_mb 调小到 5-10让通信更早开始。另一个被忽略的参数是 gradient_as_bucket_viewTrue这个选项能让梯度直接复用 bucket 的内存视图减少一次梯度的拷贝既省显存又减少通信准备时间。5.2 调大 per-GPU batch size摊薄通信频次这一步看起来最简单收益往往最大。多卡训练的通信是每个 step 都发生的所以减少 step 总数就能直接减少通信总次数。固定总训练样本数不变的前提下增大 per-GPU batch size意味着每个 step 里计算时间变长通信时间占比变小扩展效率自然提升。我用过一个实际案例一个 7B 规模模型的微调任务per-GPU batch 从 8 调到 32 之后双卡扩展效率从 76% 上升到了 91%。原因就是这个任务单卡 step 时间只有 1.2 秒通信时间占了 0.25 秒占比超过 20%把 batch 调到 32 后单卡 step 时间拉长到 4.2 秒通信时间基本不变占比降到 6%。当然增大 batch 要考虑显存容量如果显存放不下可以用梯度累积gradient accumulation达到类似效果。注意梯度累积只是把多个 micro-batch 的梯度累加后再更新它不会减少 AllReduce 的通信次数反而会增加所以不做专门处理时扩展效率并不会因此提升。5.3 数据流水线优化与随机种子一致性CPU 数据预处理和多卡步调不一致的问题在很多小型工作站上比通信问题更致命。两条建议第一把 DataLoader 的 num_workers 设置成 CPU 核心数的 1/2 到 2/3不要盲目设成核心数满值否则 CPU 调度开销反而拖慢数据供给第二开启 persistent_workersTrue 和 prefetch_factor 适当调大能显著减少每个 epoch 切换时的数据加载抖动。随机种子一致性这个问题很多人不在意。分布式训练启动时如果每个进程的 DataLoader shuffle 种子不一致每张卡拿到的数据顺序就不同。表面上看没什么大问题但只要某个 batch 里样本的 padding 长度差异很大就会导致卡的算力负载非常不均衡。我会在初始化分布式环境后设置 torch.manual_seed(0)并且给每个进程加一个 rank 偏移量保证数据打乱顺序一致但各自取不同切片。这个操作不花时间却能让双卡利用率曲线的两条线明显靠拢。5.4 验证优化效果固定方法学重复测量优化做完马上回到第二章的测量方法学重新跑一次单卡和双卡的对比测试。我习惯把每一次优化前后的扩展效率记录在一个表格里包括配置项、step 时间、通信占比、数据加载时长等。这样既方便自己复盘也为团队汇报留下了可复现的证据。建议记录的信息GPU 型号和驱动版本以及 NCCL 版本模型大小、per-GPU batch size、精度模式FP32/FP16/BF16单卡稳定 step 时间、双卡稳定 step 时间每秒处理样本数samples/sec扩展效率百分比profiler 观察到的通信时间占比这套流程走下来你就不会再陷入“改了一堆设置感觉变快了但不知道有多快”的迷雾里。6. 我的实测参考值与最终建议给一个我多次实验总结出的参考区间方便读者初步判断自己的效率值是否正常。单机双卡、NVLink 或 PCIe x16 互联、模型规模在 1B-10B 之间、固定 per-GPU batch 的情况下扩展效率落在 85%-95% 都属于健康范围。超过 95% 的很少见除非模型极小、通信占比极低。低于 80% 就需要排查尤其是先确认 P2O 通信路径和通信重叠是否正常。如果是双机多卡情况会差很多。跨节点通信要走网络比如 InfiniBand 或 RoCE带宽和延迟都比机内链路差一个量级8 卡跨两机的扩展效率能保持 60%-75% 就算不错了。很多训练框架会在跨节点场景使用梯度压缩、分片通信等技巧就是因为网络通信成本太高不得不做上层优化。单机双卡是学习多卡扩展效率最好的起步环境因为变量少、链路简单、瓶颈容易定位。最后分享一个我个人的实测习惯每次跑完一轮扩展效率测试我会把用到的所有环境变量、软件版本、硬件拓扑信息原样存档因为这类问题的坑往往藏在意想不到的地方。比如你有一次重装驱动后扩展效率变了但翻遍代码都找不到原因最后发现是 NCCL 的环境变量配置变了。用文档把这些固定住能让每一轮排查都建立在可复现的基础上而不是靠运气。希望这套测量和分析方法能让你下一次插上第二张卡时心里有数——不光知道它够不够快更知道它为什么这么快、为什么没更快。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。