Skip to main content

并行

https://zhuanlan.zhihu.com/p/1937556222371946860

NVIDIA NeMo Framework https://docs.nvidia.com/nemo-framework/user-guide/latest/nemotoolkit/features/parallelisms.html

  • 有几张动图可以看看,有一些其他文档没讲的并行方式,还有一些工程上的概念

MOE动图: https://pytorch.org/blog/training-moes/

在大模型(LLM)训练和推理中,随着上下文窗口(Context Window)的不断扩大,单张 GPU 的显存和计算能力迅速成为瓶颈。

1. Sequence Parallelism (SP) — 序列并行

核心关键词: Tensor Parallelism (TP) 的扩展非线性层优化显存节省

为什么需要 SP?

在传统的 Tensor Parallelism (TP)(如 Megatron-LM 的早期版本)中,虽然巨大的矩阵乘法(Linear Layers)被切分到了多个 GPU 上并行计算,但在两个 Tensor Parallel 模块之间,通常存在一些未被切分的操作,例如 LayerNorm(层归一化)和 Dropout

  • TP 的问题: 在这些未切分的层上,每个 GPU 都保存了完整的序列副本(duplicated inputs)。这意味着,如果序列长度(Sequence Length)很长,这部分的激活值(Activation Memory)会占据大量显存,且存在重复计算。

SP 如何工作?

Sequence Parallelism 正是为了解决这个问题。它并没有改变矩阵乘法(GEMM)部分的切分方式,而是在那些之前没有并行化的部分(LayerNorm, Dropout),沿着序列维度(Sequence Dimension)进行切分。

  • 流程变化:
    • 原 TP: All-Reduce(同步完整结果) -> LayerNorm(每张卡都算完整的) -> 下一层。
    • SP: Reduce-Scatter(每张卡只拿一部分序列结果) -> LayerNorm(每张卡只算自己那部分序列) -> All-Gather(收集完整序列用于下一次矩阵乘法)。
  • 收益: 这里的 LayerNorm 和 Dropout 的激活显存被分摊到了 N 张卡上,显著减少了显存占用,允许训练更长的序列。

2. Context Parallelism (CP) — 上下文并行

核心关键词: Attention 计算切分全层切分超长上下文

为什么需要 CP?

SP 虽然节省了 LayerNorm/Dropout 的显存,但并没有解决**Attention(注意力机制)**本身的计算瓶颈。Attention 的计算复杂度与序列长度的平方成正比(O(N2)O(N^2))。当序列长度达到 100K 或 1M 时,单张 GPU 根本无法存储 KV Cache,也无法完成 Attention 矩阵的计算。

CP 如何工作?

Context Parallelism 是一种更彻底的序列维度切分。它不仅仅处理 LayerNorm,而是将输入的 Tensor 在序列维度上彻底切分给不同的 GPU,让每个 GPU 负责序列的一部分(例如:GPU0 负责第 1-8000 个 token,GPU1 负责第 8001-16000 个 token)。

  • 核心难点(Attention): 计算 Attention 时,GPU0 的 token 需要去“关注” GPU1 上的 token(计算 Q×KTQ \times K^T)。

  • 实现方式: CP 通常通过精细设计的通信机制来解决这个问题,常见的实现包括:

    • Ring Attention: GPU 之间通过环形通信传递 KV 块,分块计算 Attention Score 并累加。
    • DeepSpeed Ulysses (All-to-All): 将序列切分转换为 Head 切分(All-to-All 通信),算完 Attention 后再转回序列切分。
  • 收益: 能够处理极长的上下文(Context)。只要 GPU 足够多,理论上可以无限扩展序列长度。


3. SP 与 CP 的核心区别对比

特性Sequence Parallelism (SP)Context Parallelism (CP)
主要目标消除 Tensor Parallel 中的显存冗余(Redundancy)。解决超长序列下 Attention 计算和 KV Cache 存不下的问题。
切分对象主要是 LayerNorm、Dropout 等在 TP 中未被切分的部分。针对所有层,尤其是核心的 Attention 计算部分。
通信模式依赖于改进的 TP 通信(将 All-Reduce 拆分为 Reduce-Scatter + All-Gather)。依赖于 P2P (Ring) 或 All-to-All 通信。
拓扑关系通常在单机内(Intra-node)的 GPU 之间进行,因为带宽要求极高。可以跨机(Inter-node)扩展,特别是 Ring Attention 对带宽容忍度相对较好。
一句话总结"TP 的补丁":让 TP 更省显存。"Attention 的分身":让多卡合力算一个超级大的 Attention 矩阵。

总结

  • 如果你在使用 Megatron-LM 进行标准的模型并行训练,开启 SP 可以帮你节省显存,让你能跑稍微长一点的序列或更大的 Batch Size。
  • 如果你要训练或推理 200k、1M 这种超长上下文模型,CP 是必须的,因为它真正解决了 Attention 算不动的问题。

Context并行

https://mp.weixin.qq.com/s/8fslGx6DjCL69bjoPvRYIA

learn

多batch的好处:

sglang two batch overlap how to realize

https://github.com/sgl-project/sglang/pull/8144

SBO (Stream-Based Overlap): 在单批次内通过 CUDA 流实现专家计算和通信的细粒度重叠。

layers/attention/tbo_backend.py

srt/batch_overlap/two_batch_overlap.py

IB_DEVICES=$(find /dev/infiniband/* -maxdepth 1 -not -type d | xargs -I{} echo '--device {}:{}')
docker run ${IB_DEVICES} --gpus all --ipc=host --cap-add SYS_NICE --cap-add IPC_LOCK -v /home/xxx/:/workspace --name xxx_vllm --entrypoint bash -it vllm-openai:0.8.0

docker run --gpus all --ipc=host -v /data/:/workspace --name xxx_vllm --entrypoint bash -it vllm/vllm-openai:v0.8.3

docker run --gpus all --ipc=host -v /home/xxx/:/workspace --name xxx_vllm --entrypoint bash -it vllm-openai:0.8.0
git clone https://github.com/deepseek-ai/DeepEP.git
wget https://developer.nvidia.com/downloads/assets/secure/nvshmem/nvshmem_src_3.2.5-1.txz
tar xf nvshmem_src_3.2.5-1.txz

######################## 安装NVSHMEM ########################
cd nvshmem_src
git apply ../DeepEP/third-party/nvshmem.patch

# 注:以下参数和DeepEP官方的不太一样,因为我们的机器需要这样设置才能使用internode功能
export NVSHMEM_IBGDA_SUPPORT=1
export NVSHMEM_IBRC_SUPPORT=1

export NVSHMEM_SHMEM_SUPPORT=0
export NVSHMEM_UCX_SUPPORT=0
export NVSHMEM_USE_NCCL=0
export NVSHMEM_PMIX_SUPPORT=0
export NVSHMEM_TIMEOUT_DEVICE_POLLING=0
export NVSHMEM_BUILD_TESTS=0
export NVSHMEM_BUILD_EXAMPLES=0
export NVSHMEM_MPI_SUPPORT=0
export NVSHMEM_BUILD_HYDRA_LAUNCHER=0
export NVSHMEM_BUILD_TXZ_PACKAGE=0
export NVSHMEM_TIMEOUT_DEVICE_POLLING=0

export NVSHMEM_DIR="${HOME}/nvshmem"
export LD_LIBRARY_PATH="${NVSHMEM_DIR}/lib:$LD_LIBRARY_PATH"
export PATH="${NVSHMEM_DIR}/bin:$PATH"
export CUDA_HOME=/usr/local/cuda

export NVSHMEM_USE_GDRCOPY=1
export GDRCOPY_HOME=/root/paddlejob/gdrcopy

cmake -G Ninja -S . -B build -DCMAKE_INSTALL_PREFIX="${NVSHMEM_DIR}"
cmake --build build/ --target install


######################## 安装DeepEP ########################
cd ../DeepEP
python setup.py build
python setup.py install

python tests/test_intranode.py
# python tests/test_internode.py 这个需要多机,见下面
python tests/test_low_latency.py
# https://blog.csdn.net/eloudy/article/details/143486017
https://github.com/NVIDIA/gdrcopy/archive/refs/tags/v2.5.1.tar.gz

$ sudo apt install build-essential devscripts debhelper fakeroot pkg-config dkms

$ cd packages
$ CUDA=/usr/local/cuda ./build-deb-packages.sh
cp -r /usr/src/nvidia-550.142 .
export NVIDIA_SRC_DIR=/workspace/examples/ep/nvidia-550.142/nvidia
# $ make prefix=<install-to-this-location> CUDA=/usr/local/cuda all install
$ make CUDA=/usr/local/cuda all install
make CUDA=/data/xxx/examples/ep/cuda-12.4 all install
$ sudo ./insmod.sh

多机用法:

START_RANK=2
END_RANK=4
if [[ ${PADDLE_TRAINER_ID} -lt $START_RANK ]]; then exit; fi
if [[ ${PADDLE_TRAINER_ID} -ge $END_RANK ]]; then exit; fi

export WORLD_SIZE=$(($END_RANK - $START_RANK))
export RANK=$(($PADDLE_TRAINER_ID - ${START_RANK}))
echo "rank: ${RANK}, nnodes: ${WORLD_SIZE}"

unset PADDLE_ELASTIC_JOB_ID PADDLE_TRAINER_ENDPOINTS DISTRIBUTED_TRAINER_ENDPOINTS FLAGS_START_PORT PADDLE_ELASTIC_TIMEOUT
for name in `env | grep -E 'PADDLE|ENDPOINT' | awk -F'=' '{print $1}'`; do unset ${name}; done

export MASTER_ADDR="10.54.101.209"
export MASTER_PORT=58978

export FLAGS_eager_communication_connection=1

export NCCL_DEBUG=WARN
export NCCL_IB_QPS_PER_CONNECTION=8
export NCCL_IB_GID_INDEX=3
export NCCL_NVLS_ENABLE=0
export NCCL_IB_GID_INDEX=3

export NVSHMEM_IB_GID_INDEX=3
export NVSHMEM_IB_TRAFFIC_CLASS=162
export NVSHMEM_BOOTSTRAP=UID
export NVSHMEM_BOOTSTRAP_UID_SOCK_IFNAME=eth0

python tests/test_internode.py

NVSHMEM

######################## 安装nvshmrun ########################
cd nvshmem_src/scripts
export NVSHMEM_HOME="${HOME}/nvshmem"
./install_hydra.sh . "${NVSHMEM_HOME}"

######################## 编译HelloWorld ########################
将 https://docs.nvidia.com/nvshmem/api/using.html 里面的第一份示例代码保存为 nvshmem_hello.cu

nvcc -rdc=true -ccbin g++ -gencode=arch=compute_90,code=sm_90 \
-I $NVSHMEM_HOME/include nvshmem_hello.cu -o nvshmem_hello.out \
-L $NVSHMEM_HOME/lib -lnvshmem -lnvidia-ml -lcuda -lcudart

######################## 运行HelloWorld ########################
nvshmrun -n 4 ./nvshmem_hello.out

MoE 和大规模 EP 并行相较于传统的 dense 架构,对整体基础设施和通信优化带来了新的挑战。特别是,大规模 EP 必然会引入跨节点的两次 all-to-all 传输。在训练阶段如何提升吞吐量,在推理阶段如何降低时延,并有效隐藏这些通信,成为基础架构设计的关键考量。

proxychains4 git clone https://github.com/NVIDIA/cuda-samples.git
CUDACXX=/usr/local/cuda-12/bin/nvcc cmake -B build -S .
CUDACXX=/usr/local/cuda-12/bin/nvcc cmake --build build --target p2pBandwidthLatencyTest
# vim Samples/5_Domain_Specific/p2pBandwidthLatencyTest/CMakeLists.txt 删除多余的arch