序列并行#

什么是序列并行#

序列并行(Sequence Parallelism,SP)最初由 Megatron 提出,其初衷是减少训练时的激活内存。核心改动是将 Allreduce->LayerNorm 改为 ReduceScatter->LayerNorm->Allgather。这项技术后来被 vllm 应用于推理。需要注意的是,将 Allreduce 拆分为 ReduceScatter 和 Allgather 本身并不会带来性能收益;它减少了 LayerNorm 的计算量,但这种收益微乎其微。SP 的真正收益来自:

  1. LLM 推理部署常使用量化。以 NPU 上常用的 INT8 量化为例,在 LayerNorm 之后,Quant 算子会将隐藏状态从 BF16 量化为 INT8。此时 Allgather 的通信量减半,耗时也几乎减半。

  2. ReduceScatter 和 Allgather 可以分别与前后 Matmul 操作融合为通信-计算并行算子,从而降低延迟。

如何使用#

目前,vllm-ascend 已基于 Inductor pass 为 VL 类模型实现了序列并行。可以通过以下方式启用:

vllm serve Qwen/Qwen3-VL-2B-Instruct \
    --tensor-parallel-size 2 \
    --compilation-config '{"pass_config": {"enable_sp": true , "sp_min_token_num": 1000}}'
  • "enable_sp":这是 SP 的开关。由于 SP 依赖于图模式,因此在 eager 模式下不受支持。

  • sp_min_token_num(来自上游 vllm 的 pass_config):根据我们的实验,当 token 数量较少(经验值小于 1000)时,SP 实际上可能带来负面影响。这是因为当通信量较小时,通信算子的固定开销成为主导因素。SP 仅在 num_tokens >= sp_min_token_num 时生效。在 Ascend 上默认值为 1000,通常无需修改。 如需自定义,请使用 --compilation-config '{"pass_config": {"enable_sp": true, "sp_min_token_num": 512}}'。该值将被追加到 compile_ranges_split_points 中,用于分割图编译范围,并检查每个范围是否适用该 pass。

在不修改 sp_min_token_num 的情况下,启用 SP 最简单且推荐的方式是:

vllm serve Qwen/Qwen3-VL-2B-Instruct \
    --tensor-parallel-size 2 \
    --compilation-config '{"pass_config": {"enable_sp": true}}'

SP 与 Flash Comm V1 的区别#

Flash Comm V1 (FC1) 是基于 NPU 开发的序列并行增强版本。其增强包括:

  1. 对于使用 MLA 结构的模型,Allgather 被推迟到 QKV 投影之后,进一步减少了通信量。

  2. 对于 MoE 模型,Allgather 被推迟到 Gating+DynamicQuant 之后,同样旨在减少通信量。

FC1 是 vllm-ascend 中独特的优化,目前基于 Custom OP 实现,但难以支持 VL 类模型(原因详见 [RFC]: support sequence parallelism by pass)。因此,目前 FC1 和 SP 是互补的。

支持矩阵#

无量化#

VL + 稠密

VL + MoE

非 VL + 稠密

非 VL + MoE

序列并行

图模式

图模式

x

x

Flash Comm V1

x

x

eager/图模式

eager/图模式

带量化#

SP 目前不支持量化,正在适配中。

VL + 稠密

VL + MoE

非 VL + 稠密

非 VL + MoE

序列并行

x

x

x

x

Flash Comm V1

x

x

eager/图模式

eager/图模式

Pass 设计#

启用 SP 时,以下 pass 按顺序运行:先 SequenceParallelismPass,然后 SequenceParallelismMoePass

SequenceParallelismPass#

首先运行 NoOpEliminationPass 以消除冗余的类视图操作,然后应用基于 AllReduce 的模式:

模式

匹配

替换

MiddleAllReduceRMSNormPattern

all_reduce + layernorm

reduce_scatter + layernorm + all_gather

LastAllReduceRMSNormPattern

相同(最后一层,无残差)

相同

Qwen3VLMiddleAllReduceRMSNormPattern

all_reduce + add + layernorm

reduce_scatter + chunk(deepstack_input_embeds) + add + layernorm + all_gather

为什么 Qwen3 VL 需要 Qwen3VLMiddleAllReduceRMSNormPattern 特殊处理

Qwen3-VL 的中间层在 all_reducelayernorm 之间插入了一个额外的 add 操作:hidden_states=hidden_states + deepstack_input_embeds。在 SP 下,hidden_states(即 input)被 reduce-scatter 到每个 rank 的形状 [seq_len/tp, hidden],而 deepstack_input_embeds 来自视觉/deepstack 路径,并保持全序列形状 [seq_len, hidden](通常在 TP rank 间复制)。简单地执行 reduce_scatter(input) + deepstack_input_embeds 会导致形状不匹配。解决方法是按 tp_sizedeepstack_input_embeds 进行 chunk,使得每个 rank 使用 add(reduce_scatter, chunk(deepstack_input_embeds)[tp_rank]),从而在 layernormall_gather 之前保持形状一致。

SequenceParallelismMoePass#

应用 SequenceParallelismPass 后,MoE 模型的计算图如下所示:

AllGather EP 计算图

概述

  1. 推迟 allgather:在 SP 下,residual 被张量并行切分。这导致下一层 layernorm 中隐藏状态和残差的形状不匹配:隐藏状态被聚集(全序列),而残差保持切分状态。解决方法是将 all_gather 移动到 layernorm 之后,使得 layernorm 在每个 rank 上操作一致的形状。MiddleLayerAllgatherAddRMSNormPatternLastLayerAllgatherRMSNormPatternQwen3VLMiddleLayerAllgatherAddRMSNormPattern 就是为此设计的,每个处理不同的层和结构变体(见下表)。

  2. AllGatherChunkNoOp 清理:当启用 MoE SP 时,vllm 引入了一个 sequence_parallel_chunk 算子(对应图中的 sp_chunk)。它与前面的 all_gather 一起形成了一个冗余的无操作(all_gather 聚集,然后 chunk 重新分割)。AllGatherChunkNoOpPattern 将这对操作替换为恒等操作,以消除冗余的通信和计算。

模式详情:

模式

匹配

替换

MiddleLayerAllgatherAddRMSNormPattern

all_gather + slice + layernorm

layernorm + all_gather

LastLayerAllgatherRMSNormPattern

相同(最后一层,无残差)

相同

Qwen3VLMiddleLayerAllgatherAddRMSNormPattern

all_gather + slice + add + layernorm

add(chunk) + layernorm + all_gather

AllGatherChunkNoOpPattern

all_gather + sequence_parallel_chunk_impl

恒等操作(无操作)

常见问题#

Q1: SP 是否默认启用?#

不,SP 默认未启用。SP 目前处于实验阶段,未来将默认启用。

代码中 enable_sp 的处理流程如下:

  • pass_config 中,enable_spsp_min_token_num 默认为 None

  • NPUPlatform.apply_config_platform_defaults:如果 enable_spTruesp_min_token_num 为 None,则设置默认的 sp_min_token_num(Dense 模型为 1000,MoE 模型为 1)

  • VllmConfig._apply_optimization_level_defaults:对于 Dense 模型,enable_sp 被设置为 True

  • VllmConfig.__post_init__:如果 sp_min_token_num 仍为 None,则 enable_sp 被设置为 False