引言

以前在训练模型的时候大部分使用的是单机单卡进行训练测试,真正使用单机多卡和多机多卡的时候,很少去实操到分布式的训练推理,本身这块对应硬件的成本高,对于个人是很少可以去把玩上的,更何况现在训练大模型大部分使用的PT好的模型,进行微调和蒸馏,或者量化部署,随着deepseek系列模型的开源,对模型的训练微调和部署的需求增多,里面涉及到的分布式训练策略,怎样对模型和数据进行拆分进行多卡sm并行处理, 这里简单介绍下相关的分布式训练推理并行策略,以及对应的代码,代码主要使用pytorch进行实现,采用单节点的2个gpu。(如果有足够多的资源,可以复现下ZeRO或者Megatron中的实现)

💡使用 PyTorch 进行模型训练的过程:

PyTorch 将值组织成Tensor , Tensor是具有丰富数据操作操作的通用 n 维数组。Module 定义从输入值到输出值的转换,其在正向传递期间的行为由其forward成员函数指定。Module 可以包含Tensor作为参数。例如,线性模块包含权重参数和偏差参数,其正向函数通过将输入与权重相乘并添加偏差来生成输出。应用程序通过在自定义正向函数中将本机Module (例如线性、卷积等)和Function (例如relu、pool 等)拼接在一起来组成自己的Module 。典型的训练迭代包含使用输入和标签生成损失的前向传递、用于计算参数梯度的后向传递以及使用梯度更新参数的优化器步骤。更具体地说,在正向传递期间,PyTorch 会构建一个自动求导图来记录执行的操作。然后,在反向传播中,它使用自动梯度图进行反向传播以生成梯度。最后,优化器应用梯度来更新参数。训练过程重复这三个步骤,直到模型收敛。

并行策略主要分为以下几种:

Data Parallelism (DP) 包括:

  • Distributed Data Parallelism(DDP)
  • Fully-Shared Data Parallelism (FSDP)

Model Parallelism(MP)包括:

  • Tensor Parallelism(TP, 有些论文中将TP描述成MP,比如Megatron-LM, ZeRO)

  • Pipeline Parallelism (PP)

  • Expert Parallelism (EP)

  • Activation Partitioning 包括:(SP和CP两者通常和TP一起使用)

    • Sequence Parallelism (SP): 针对序列切分,在模块的输入和输出侧对序列进行切分,常使用在LayerNorm,Dropout(通过all_gather,reduce-scatter 通信计算,减少训练时激活值 gpu内存占用,以前是每个卡上单独的副本LayerNorm,Dropout,冗余gpu内存)
    • Context Parallelism (CP): 主要针对Transformer模型长序列的训练。

DP和MP的比较:

  • DP 的扩展效率比 MP 更好,因为 MP 降低了计算的粒度,同时也增加了通信开销。超过某个点后,较低的计算粒度会降低每个 GPU 的效率,而增加的通信开销会阻碍跨 GPU 的可扩展性,尤其是跨越节点边界时。相反,DP 既具有更高的计算粒度,又具有更低的通信量,从而可以实现更高的效率。

  • DP 内存(GPU HBM)效率低,因为模型状态在所有数据并行进程中冗余存储。相反,MP 对模型状态进行分区,以提高内存效率。

  • DP和MP都保存了整个训练过程中需要的所有模型状态,但并不是所有状态都是一直需要的,比如每一层对应的参数只在该层的前向传播和后向传播时才需要。

模型训练的并行策略同样适用于推理测的并行策略,但是调度和执行的方式稍有不同。这里主要是结合pytorch 实现简单的多卡模型并行策略训练。

相关的分布式训练推理并行策略,以及对应的代码运行操作demo,可以查看这个PR:

https://github.com/ai-bot-pro/achatbot/pull/127 (如有不对,欢迎指出~)

Data Parallelism (DP) 数据并行

并行化是大规模训练大型模型的关键策略。对于适合在设备内存中进行训练的模型,数据并行 (DP) 用于将训练扩展到多个设备。在 DP 中,模型参数在每个设备上复制。在每个步骤中,一个小批量被均匀地划分到所有数据并行进程中,这样每个进程都会在不同的数据样本子集上执行前向和后向传播,并使用跨进程的平均梯度在本地更新模型。

Distributed Data Parallelism(DDP)

在同一台机器上使用多个 GPU 进行单进程多线程数据并行训练的DataParallel 、用于跨 GPU 和机器进行多进程数据并行训练的DistributedDataParallel以及用于通用分布式模型并行训练。

Distributed Data-Parallel (DDP):适用于多 GPU 或多节点环境。每个进程拥有完整的模型副本,处理不同的数据子集,计算完成后通过 All-Reduce 操作同步梯度。

从 v1.5 开始,PyTorch 本身提供了几种加速分布式数据并行的技术,包括存储梯度、将计算与通信重叠以及跳过梯度同步。具体内容参考这篇论文:2020.6 PyTorch Distributed: Experiences on Accelerating Data Parallel Training

具体操作见:

分布式数据并行 (DDP)是 PyTorch 中的一个强大模块,可让你在 多台机器,使其非常适合大规模深度学习应用。 要使用 DDP,您需要生成多个进程并为每个进程创建一个 DDP 实例。

但它是如何工作的?DDP 使用来自 torch.分布式 包来同步所有进程的梯度和缓冲区。这意味着每个进程将有 它有自己的模型副本,但它们都会协同工作来训练模型,就像在一台机器上一样。

为了实现这一点,DDP 为模型中的每个参数注册了一个 autograd 钩子。当运行反向传递时,此钩子会触发并触发所有进程之间的梯度同步。这可确保每个进程具有相同的梯度,然后使用这些梯度来更新模型。

  • rank 为 0 的进程广播到组中的所有其他进程,以确保所有模型副本都从完全相同的状态开始
  • 每个 DDP 进程创建一个本地Reducer ,负责反向传播期间的梯度同步
  • 为了提高通信效率, Reducer将参数梯度组织成 bucket,并一次减少一个 bucket,bucket_cap_mb参数来配置 bucket 大小
  • 前向激活参数,后向计算梯度,优化器同步更新每轮迭代学习到的参数,每个处理进程需要同步平均梯度(allreduce)

from:

Fully-Shared Data Parallelism (FSDP)

Fully Sharded Data-Parallel Training (FSDP):参考了来自TF中的XLA 实现(来自2020.4 Automatic Cross-Replica Sharding of Weight Update in Data-Parallel Training), 以及DeepSpeed ZeRO-3(分片模型参数、梯度和优化器状态到多个设备)中的实现。特别适合训练超大规模模型。

FSDP 是一种数据并行训练,但与传统的数据并行不同,传统的数据并行维护模型参数、梯度和优化器状态的每个 GPU 副本,它将所有这些状态分片到数据并行工作器上,并可以选择将分片后的模型参数卸载到 CPU。

from:

下图展示了 FSDP 如何针对两个数据并行进程工作:

通常,模型层以嵌套方式包裹在 FSDP 中,因此在前向或后向计算期间,只有单个 FSDP 实例中的层需要将完整参数收集到单个设备中。收集的完整参数将在计算后立即释放,释放的内存可用于下一层计算。通过这种方式,可以节省峰值 GPU 内存,从而可以扩展训练以使用更大的模型大小或更大的批处理大小。为了进一步最大限度地提高内存效率,当实例在计算中不活跃时,FSDP 可以将参数、梯度和优化器状态卸载到 CPU。

Model Parallelism(MP)模型并行

当模型无法装入设备内存时,模型并行 (TP) 和流水线并行 (PP) 分别以垂直和水平方式将模型拆分到进程之间。

Tensor Parallelism(TP)

张量并行(Tensor Parallelism) 是一种分布式深度学习训练策略,旨在通过将神经网络中的张量(例如权重矩阵或激活值)分割到多个计算设备(如 GPU 或 TPU)上并行计算,从而加速训练并支持更大的模型。它特别适用于单设备内存不足以容纳整个模型或输入数据的情况。张量并行通常与数据并行(Data Parallelism)、流水线并行(Pipeline Parallelism)等策略结合使用,以实现高效的大规模训练。

📢

  • 使用pytorch 封装好的DeviceMesh来管理切分权重的维度,不会对模型的结构进行并行化的修改;如果直接使用对模型结构进行并行化修改,加入通信原语进行权重梯度同步,粒度更细,但是带来处理的复杂度,可维护性会差些。
  • 对于训练上亿,万亿参数的模型,TP常结合FSDP 一起操作。
  • 使用封装好的DeviceMesh 需要特别注意定义模块的模型结构,并确认好切分维度。

from:

Pipeline Parallelism (PP)

流水线并行是深度学习的基本并行之一。它允许对模型的执行进行分区,以便多个 微批次可以同时执行模型代码的不同部分。

在pytorch中可以使用torch.distributed.pipelining 这个库,它是从PiPPy项目迁移而来的。

From:

Expert Parallelism (EP)

专家并行(Expert Parallelism) 是一种分布式深度学习训练策略,通常用于 混合专家模型(Mixture of Experts, MoE),通过将多个专家(Experts,通常是神经网络子模块)分配到不同的计算设备(如 GPU 或 TPU)上并行计算,从而提升训练效率并支持大规模模型。

📢

  • 如果使用send/recv通信原语操作,很容易产生相互等待(死锁)的情况;解决方法:
    • 使用broadcast
    • 使用 all to all的通信原语
    • 调试时,把[batch_size, seq_len, hidden_size] 这些维度降低,以便打印调试
  • pytorch GPU之间的通信,后端采用NCCL库
    • 节点内GPU通过nvlink来通信,也可以通过CPU卸载到共享内存来通信。前者更快,不需要cpu干预
    • 节点间GPU通过IB的GDR(RDMA)来通信(异步,zero copy),也可以通过TCP通信。前者更快,后者需要cpu干预

from:

Sequence Parallelism (SP)

针对序列切分,在模块的输入和输出侧对序列进行切分,常使用在LayerNorm,Dropout(通过all_gather,reduce-scatter 通信计算,减少训练时激活值 gpu内存占用,以前是每个卡上单独的副本LayerNorm,Dropout,冗余gpu内存)

from:

Context Parallelism (CP)

上下文并行(CP)是一种在序列长度维度上的并行化方案。与之前的 SP(序列并行)不同,后者仅分割 Dropout 和 LayerNorm 激活序列,CP 沿序列维度划分网络输入和所有激活。使用 CP,除注意力之外的所有模块(例如 Linear、LayerNorm 等)都可以照常工作而无需任何更改,因为它们没有 token 间操作。对于注意力,每个 token 的 Q(查询)需要使用同一序列中所有 token 的 KV(键和值)进行计算。因此,CP 需要跨 GPU 进行额外的全收集以收集完整的 KV 序列。相应地,在反向传播中,应将减少散射应用于 KV 的激活梯度。为了减少激活内存占用,每个 GPU 仅在正向存储序列块的 KV,并在反向再次收集 KV。KV 通信发生在 GPU 与其他 TP 组中的对应 GPU 之间。all-gather和reduce-scatter在底层转换为环形拓扑中的点对点通信。交换 KV 还可以利用 MQA/GQA 来减少通信量,因为它们只有一个或几个 KV 注意力头。

CP所要解决问题:

LLM 遇到长上下文(即长序列长度)的 OOM(内存不足)问题,因为激活的内存占用量呈线性增加。反向重新计算激活可以避免 OOM,但也会带来大量开销(完全重新计算约为 30%)。扩大 TP(张量模型并行性)也可以解决 OOM 问题,但它可能会使计算(例如线性)太短而无法重叠通信延迟。需要明确的是,无论是否发生 OOM,扩展到具有更大 TP 的更多 GPU 都可能遇到重叠问题。

CP 可以更好地解决这些问题。使用 CP,每个 GPU 仅计算序列的一部分,从而将计算和通信都减少了 CP 倍。因此,不必担心它们之间的重叠。每个 GPU 的激活内存占用也小了 CP 倍,因此不再存在 OOM 问题。如下图 所示,TP 和 CP 的组合可以通过消除重新计算开销并在计算和通信之间做出最佳权衡来实现最佳性能。

from:

reference

  • pytorch 分布式训练文档:https://pytorch.org/tutorials/beginner/dist_overview.html

    并行策略选择建议

    • 数据并行是一种被广泛采用的单程序多数据训练范式,其中模型在每个流程上复制,每个模型副本为不同的输入数据样本集计算局部梯度,在每个优化步骤之前在数据并行通信器组内对梯度进行平均。
    • 当模型不能加载到单个GPU 训练推理时,需要使用模型并行技术(或分片数据并行),并且可以组合在一起形成多维(N-D)并行技术。
    • 如果您的模型适合单个 GPU,但您想使用多个 GPU 轻松扩展训练,请使用DistributedDataParallel (DDP)
    • 当您的模型无法安装在一个 GPU 上时,请使用FullyShardedDataParallel (FSDP) 。请参阅: FSDP 入门
    • 如果达到 FSDP 的扩展限制,请使用张量并行 (TP)和/或管道并行 (PP) 。查看张量并行教程,参阅:TorchTitan 3D 并行端到端示例

  • ⭐️ https://lilianweng.github.io/posts/2021-09-25-train-large/ | https://openai.com/index/techniques-for-training-large-neural-networks/

  • 跟李沐读分布式训练系统工程论文: https://github.com/mli/paper-reading/?#%E7%B3%BB%E7%BB%9F

    • GPipe: PP 并行, 针对TPU v2/v3的训练DNN模型(CNN,Transformer)的场景(GPU也适用); 以及Activation Memory的优化(Activation Checkpoints, 计算换内存),后面ZeRO使用了这种方式进行优化(进行了切分,带宽-> 计算 换 内存)

    • Megatron-LM: MP(TP) Transformer Decoder(GPT-2) 3个模块 embedding(按token词表大小切成小词表,数据块中的token里有对应的词就算,没有就位0,算完进行allreduce), MHA(按头切), MLP(up project 按列切,down project按行切) 训练学习的权重是怎么切的,而且对通信要求高,ps: 讲解MLP怎么切时候,可以看下vllm TP这个图

    • ZeRO: 所要解决的内存冗余问题:

      • 混合精度的训练时内存冗余的问题(fp16/fp32) fp16用于前向,后向传播时输入输出的数据,以及模型训练参数的计算(xw+b),而梯度优化的时候,模型更新的权重使用fp32(主要是防止使用fp16的精度不足的情况,比如大数相加超出精度范围,非常小的数相加可能还是0,所以为了在后向传播结束时有效计算和应用更新,混合精度优化器会保留参数的 fp32 副本以及所有其他优化器状态的 fp32 副本),更新完之后再转成fp16进行下一次的前向和后向的计算; 假如1B的模型参数需要训练学习,使用fp16进行前向后向计算,总共需要 2B+2B的内存,假如使用ADAM作为优化器来更新权重,权重类型使用fp32, 总共需要4B+4B+4B, 所以混合精度总共需要4B+12B=16B的内存;这会导致在做前向 或者 后向梯度更新传播 的时候只会用的 2B, 其他 14B是冗余的,不参与计算。
      • 中间激活(神经网络中每一层经过激活函数(如 ReLU 或 GELU)后输出的值,这些激活值需要在训练时存储以进行反向传播)的计算,会产生大量的内存用于存储激活值。 比如 使用 1K 序列长度和 32 批次大小训练的 1.5B 参数的GPT2模型(模型结构配置见openai-community/gpt2-xl),总共需要计算存储的激活值: 12 × hidden dim × batch × seq length × transformer layers = 12*1600*32*1024*48 ,激活值的数据类型是fp16,总共2 x 12*1600*32*1024*48 = 62,914,560,000 大概需要 60GB 的内存,其中 12 是一个经验系数,代表每个 Transformer 层中激活值的“平均倍数”,用来快速估计总激活值,实际计算低于这个值(12 是一个粗略的上界估计,它可能高估了某些部分(例如未精确区分 FFN 中间层和输出层的贡献))。激活检查点(或激活重新计算)是一种常用方法(不存储所有中间激活,而是仅保存部分关键层的激活(称为检查点,Activation Checkpoints),并在反向传播时通过重新计算(Recompute)来恢复其他层的激活。这种方法通过增加少量的计算开销(重新计算前向传播),大幅减少内存占用),可将激活内存减少大约总激活的平方根,但代价是 33% 重新计算开销。这将使该模型的激活内存消耗减少到大约 8 GB。如果训练一个万亿参数级别的模型,存放激活值是一个可以优化内存的点。
      • 在做allreduce时,进行网络IO发送接收数据的时候,产生的临时buffer。
      • 使用pytorch向GPU中申请和回收内存操作时,由于没有虚拟内存的概念,会产生内存碎片的问题。(可以维护一个对象内存池)

      解决方案:(主要思想是:利用 通信带宽 访问分区存放内存,然后 重新计算 来换 空间,如果要节约训练时间,需要通信带宽好的网络,现在常用的是节点内用NVLink, 节点外用IB, 结合RDMA技术(GDR))

      • ZeRO-DP 结合了MP的特点,内存利用率(不需要冗余存储模型权重);对于模型计算的权重状态这些冗余内存,可以存放一份内存拷贝在一台GPU上,和Parameter Server 类似;ZeRO- DP对模型状态进行分区而不是复制它们,并使用动态通信计划,利用模型状态的内在时间特性,同时最小化通信量。通过这样做, ZeRO -DP 随着 DP 度数的增加线性减少模型的每个设备内存占用,同时保持通信量接近默认 DP,从而保持效率。
      • ZeRO-R :
        • Reducing Activation Memory(减少激活值存储): ZeRO通过在 GPU 上划分激活检查点来消除 MP 中的内存冗余,并使用 allgather 按需重建它们。激活内存占用量会随着 MP 度数的增加而减少。对于非常大的模型, ZeRO甚至可以选择将激活分区移至 CPU 内存,同时由于这些模型中的算术强度较大,仍能实现良好的效率。
        • Managing Temporary buffers(管理临时buffers):ZeRO -R 使用恒定大小的缓冲区来避免临时缓冲区随着模型尺寸的增加而爆炸,同时使其足够大以保持高效。
        • Managing fragmented Memory(管理碎片内存): 内存碎片是短期和长期内存对象交错的结果。在正向传播过程中,激活检查点是长期的,但重新计算的激活是短期的。同样,在反向计算中,激活梯度是短期的,而参数梯度是长期的。基于这一洞察, ZeRO通过将激活检查点和梯度移动到预先分配的连续内存缓冲区来执行即时内存碎片整理。这不仅增加了内存可用性,而且还通过减少内存分配器查找可用连续内存所需的时间来提高效率。
    • Pathways: 针对google内部TPU上的data flow的设计,支持大规模TPU集群的训练任务调度,用户使用的前端编程工具是Jax, 然后编译DAG计算图,由调度control 分发到TPU上执行(相对于SPMD , MPMD,在分布式调度执行上复杂一些, 需要优化成异步叠加并行执行,减少通信等待)。(尽管这些设计实现未开源,但是设计思路可以学习,类似Ray)


ML:

DNN:

Transformers for LLM:

MoE Transformers for LLM:


总结

  • google系统工程大部分是针对自家的TPU集群优化的场景,不过一些设计思路可以套用的GPU上,比如GPipe提出的PP以及Activation 训练时大量中间计算的激活值的内存占用优化。
  • 英伟达和微软则是针对GPU机器在DNN模型训练上做了大量的优化,而且开源了LLM训练框架和细节上的实现。
  • Meta 则是在已有基础上进行了工程上的系统组合封装,便于开发,升级和使用。在pytorch 的基础上组合多维度切分并行,易于组合训练DNN模型。

a little thinking 🤔

  • 数据和模型scaling,推动训练系统工程需要结合硬件进行优化, 现在开源的训练方案可以支持万亿级别的模型参数训练。但是需要投入大量的计算,网络存储资源,以及时间成本;算力,存储和网络的性能提高(计算快,吞吐大,延迟低),在更快的时间内训练模型,节约时间成本,但是成本高;如何减低成本,又能比较快的训练出推理质量好的模型,如果持续迭代模型的话,系统工程优化迭代可能会一直持续进行。
  • 理解了原理,大部分的操作主要在通信原语上的操作:
    • EP中的All_to_All(dist.all_to_all);
    • DDP,TP中的All_Reduce;
    • FSDP,SP,CP中的All_Gather, Reducer-Scatter
  • 训练过程其实包括了推理,就是前向的过程,所以,在推理优化时,可以复用训练时前向推理的并行策略,以及通信原语,来提高吞吐;但是推理优化,还需要考虑推理接口的延迟,减少计算,引入了KVCache, 动态批次(In-flight) 等优化技术, 具体见: https://github.com/ai-bot-pro/achatbot/pull/126#issuecomment-2700184937

附录

激活内存

可以按照这个图来计算总共训练时, 所需要的 GPU HBM(内存) 是多少, 具体前向的激活参数所占内存的计算可以见论文:

计算每层激活所占内存 计算公式:(基线, 使用TP)

加入SP 和 激活ckpt 重计算

TP + 不同技术使用的激活内存占保持所有激活在张量并行等级之间分裂所需内存的百分比

通信原语

在分布式计算和并行计算领域(特别是深度学习和MPI编程中),all-gatherreduce-scatterall-reduceall-to-all 是常见的集体通信操作,用于在多个进程或节点之间同步和交换数据。它们在功能和使用场景上有所不同,下面解释它们的含义:

1. All-Gather

  • 含义:所有进程(或节点)将各自的数据“收集”(gather)起来,并分发给所有进程,使得每个进程最终都拥有所有进程的数据集合。
  • 过程
    • 每个进程提供一个数据块(通常是一个向量或数组)。
    • 操作完成后,每个进程都会收到所有进程提供的数据块的完整副本。
  • 输出:所有进程的输出是相同的,包含所有输入数据的拼接。
  • 示例场景:假设有3个进程,每个进程有一个数字(分别是1、2、3),All-Gather后,每个进程都会得到完整的数组 [1, 2, 3]。
  • 用途:常用于需要全局信息的情况,比如收集所有节点的计算结果。

2. Reduce-Scatter

  • 含义:先对所有进程的数据进行“归约”(reduce,例如求和、求最大值等),然后将归约后的结果“分散”(scatter)到各个进程上,每个进程只接收一部分结果。
  • 过程
    • 每个进程提供一个数据块。
    • 对所有数据块按某种归约操作(如求和)进行计算,得到一个总结果。
    • 将总结果分割成若干部分,分别分发给各个进程。
  • 输出:每个进程收到归约结果的一部分,部分之间互不重叠。
  • 示例场景:假设有3个进程,数据分别为 [1, 4, 7]、 [2, 5, 8]、 [3, 6, 9],Reduce-Scatter(求和)后:
    • 总和为 [6, 15, 24]。
    • 进程0得到6,进程1得到15,进程2得到24。
  • 用途:适用于需要分布式存储归约结果的场景,比如梯度计算中的分散存储。

3. All-Reduce

  • 含义:对所有进程的数据进行“归约”(reduce),然后将归约后的结果广播给所有进程,使得每个进程都拥有完整的归约结果。
  • 过程
    • 每个进程提供一个数据块。
    • 对所有数据块执行归约操作(如求和、求平均等)。
    • 将归约后的结果分发给所有进程。
  • 输出:所有进程的输出是相同的,包含完整的归约结果。
  • 示例场景:假设有3个进程,数据分别为1、2、3,All-Reduce(求和)后,每个进程都会得到结果6。
  • 用途:广泛用于分布式机器学习中,比如同步梯度(如深度学习中的参数更新)。

4. All-to-All

  • 含义:每个进程(或节点)向所有其他进程(包括自己)发送独特的数据,同时从所有其他进程接收数据。也就是说,每个进程都有一个输入数据块,针对每个目标进程会有特定的数据片段。
  • 过程
    • 每个进程准备一个数据数组,数组的长度通常等于进程数,每个位置对应一个目标进程的数据。
    • 操作完成后,每个进程收到一个新的数据数组,数组中的每个元素是从对应发送进程接收到的数据。
  • 输出:每个进程获得一个完整的数据数组,数组中的数据来自所有进程的特定部分。
  • 示例场景
    • 假设有3个进程:
      • 进程0的输入是 [A0, B0, C0](分别发给进程0、1、2)。
      • 进程1的输入是 [A1, B1, C1]。
      • 进程2的输入是 [A2, B2, C2]。
    • All-to-All 操作后:
      • 进程0收到 [A0, A1, A2]。
      • 进程1收到 [B0, B1, B2]。
      • 进程2收到 [C0, C1, C2]。
  • 用途:常用于需要全局数据交换的场景,比如矩阵转置、傅里叶变换(FFT)或某些分布式算法中的数据重排。
  • All-to-All 的特点
    • 与 All-Gather 不同,All-to-All 允许每个进程发送不同的数据给不同的目标进程,而不是简单地广播或收集相同数据。
    • 通信量:通信复杂度较高,通常是 O(N²),其中 N 是进程数,因为每个进程都要与所有其他进程交换数据。
    • 对称性:输入和输出的数据量通常是对称的,每个进程发送和接收的数据量相等。

对比

操作 输入 输出 通信目标
All-Gather 每个进程提供部分数据 每个进程获得所有数据的副本 收集全局数据
Reduce-Scatter 每个进程提供部分数据 每个进程获得归约结果的一部分 归约后分散
All-Reduce 每个进程提供部分数据 每个进程获得完整的归约结果 归约后广播
All-to-All 每个进程提供针对每个目标的独特数据 每个进程获得来自所有进程的独特数据 全局个性化数据交换