@RunLLM 为什么pp并行当中, recv_tensor_dict 函数里,用到了all_gather,pp并行不是只需要接收上一个rank的数据吗,而all_gather不是接收所有rank的数据?
我的理解是,pp并行时recv_tensor_dict确实主要用于接收上一个rank的数据,但在vLLM实现中,为了提升性能,部分张量(如在TP组内分片的张量)会用all_gather优化:每个rank只发送自己的一部分,接收端通过all_gather重构完整张量。这样可以减少通信量,提高效率。并不是所有数据都用all_gather,只有满足分片条件的张量才会用此优化。详见API说明。
需要详细举例说明哪些张量会用all_gather吗?
Sources:
Hint: Mention @RunLLM in the post for followups.
@RunLLM reorder_batch 是干什么的
reorder_batch 的作用是根据注意力后端(如MLA、FlashAttention等)的需求,对当前batch中的请求顺序进行调整,以提升推理效率。例如,可以将decode和prefill类型的请求分组,或根据计算/内存瓶颈进行排序,从而优化GPU利用率和吞吐量。API文档说明 和 源码注释均有明确描述。
需要详细举例说明reorder_batch的具体策略和场景吗?
Sources:
Hint: Mention @RunLLM in the post for followups.
@RunLLM 能举个例子吗 讲讲如何重排的?
当然可以。以reorder_batch在FlashAttention场景下为例:假设当前batch有4个请求,分别是2个prefill(填充)请求和2个decode(解码)请求。为了提升FlashAttention的效率,reorder_batch会将同类型的请求排在一起,比如先排列2个prefill,再排列2个decode,这样可以让底层attention kernel批量处理同类请求,减少kernel切换和提升吞吐量。源码PR示例。
需要更详细的代码示例或其他场景说明吗?
Sources:
- https://github.com/vllm-project/vllm/issues/23223
- https://docs.vllm.ai/en/latest/api/vllm/transformers_utils/repo_utils/#vllm.transformers_utils.repo_utils.list_repo_files
Hint: Mention @RunLLM in the post for followups.
@RunLLM 在cudagraph 模式下 出现了 max_seqlen_q 不是最大值,导致FA算子vm fault,在pp并行的模型下,这个分析可能是什么原因造成的
在CUDA Graph模式下,pp并行时max_seqlen_q不是最大值,导致FlashAttention算子VM fault,常见原因是:捕获CUDA Graph时的max_seqlen_q与实际推理时的不一致,尤其在pipeline并行下,不同stage的输入长度动态变化,导致已捕获的graph无法覆盖更长的序列,进而引发非法内存访问(VM fault)。官方设计文档明确指出,捕获graph时需保证max_query_len等元数据与实际推理场景严格一致,否则会出现此类问题。
需要详细分析pp并行下max_seqlen_q不一致的具体触发链路吗?
Sources:
Hint: Mention @RunLLM in the post for followups.
@RunLLM CUDA Graph时, max_seqlen_q是如何计算的,和eager模式下,有什么不同
在CUDA Graph模式下,max_seqlen_q是在捕获graph时由调度器根据batch的最大query长度(如uniform_decode时为固定长度)预先设定的,捕获后每次重放都必须与捕获时一致;而在eager模式下,max_seqlen_q是根据当前实际输入动态计算的,可以每次变化。官方设计文档有明确说明。
需要详细举例说明两种模式下max_seqlen_q的计算流程吗?
Sources:
Hint: Mention @RunLLM in the post for followups.
@RunLLM 举例说明两种模式下max_seqlen_q的计算流程吧
我的理解是,在CUDA Graph模式下,max_seqlen_q的计算流程是:调度器在捕获graph时会根据当前batch中所有请求的最大query长度(如batch中最长的序列长度)设定max_seqlen_q,捕获后每次重放都必须用同样的max_seqlen_q,否则会出错。而在eager模式下,max_seqlen_q每次都是根据当前实际输入动态计算的,可以随着batch内容变化而变化,无需和历史保持一致。设计文档说明。
举例:
- CUDA Graph模式:假设捕获时batch最大长度为128,则max_seqlen_q=128,之后每次重放都必须是128。
- Eager模式:本轮batch最大长度为64,则max_seqlen_q=64;下轮batch最大长度为256,则max_seqlen_q=256,动态变化。
需要更详细的源码流程或伪代码示例吗?
Sources:
- https://github.com/vllm-project/vllm/blob/main/vllm/v1/worker/gpu_model_runner.py
- https://docs.vllm.ai/en/latest/api/vllm/v1/attention/backends/mla/flashattn_mla/#__span-0-116
Hint: Mention @RunLLM in the post for followups.
rank=6, query_start_loc: tensor([ 0, 1479, 4000, 2155, 3144, 4000, 0, 0, 0], num_reqs:2, total_num_scheduled_tokens: 4000, max_num_scheduled_tokens:2521
rank=7, query_start_loc: tensor([ 0, 546, 1950, 2155, 3144, 4000, 0, 0, 0],num_reqs:1, total_num_scheduled_tokens: 546, max_num_scheduled_tokens:546
pp并行,但最终fa 算子接收到的 max_num_scheduled_tokens 是546, 其中seq len是 1479,出现两个rank 的数据混淆了的情况,能帮忙分析一下,可能是什么原因吗
你描述的现象(pp并行下,fa算子收到的max_num_scheduled_tokens为546,但实际有seq_len=1479,且不同rank数据混乱)很可能是由于pipeline并行下各rank的batch元数据(如query_start_loc、max_num_scheduled_tokens)未正确对齐或同步,导致下游FlashAttention算子只看到最小rank的max_num_scheduled_tokens,进而出现数据错位和内存越界。相关源码和设计文档都强调,pp并行时必须保证各stage的batch描述信息严格一致,否则会导致你遇到的“数据混乱”问题。
建议排查:1)pipeline各rank的batch分配和调度是否同步,2)max_num_scheduled_tokens等元数据在pp通信时是否被正确广播/聚合,3)FlashAttention调用前是否有全局一致性检查。
需要更详细的排查建议或同步机制说明吗?
Sources:
Hint: Mention @RunLLM in the post for followups.
@RunLLM max_num_scheduled_tokens 会在各个rank之间 传递吗,不应该是各个rank间都是独立的吗
在pp(pipeline parallel)并行场景下,max_num_scheduled_tokens 在各个rank之间通常是独立的,每个rank根据自己实际分配到的batch计算自己的max_num_scheduled_tokens,并不会在rank之间直接传递或同步为同一个值。源码实现和官方文档都说明,pp各stage的batch元数据是本地独立维护的。
需要详细说明哪些情况下会出现同步或特殊处理吗?
Sources:
Hint: Mention @RunLLM in the post for followups.
@RunLLM build_for_cudagraph_capture ,这个接口是干什么的,只在dummy_run阶段用吗,实际运行,是否有用的到
build_for_cudagraph_capture 接口用于为CUDA Graph捕获阶段构建专用的attention元数据,确保捕获的graph能正确复现推理流程。它主要在dummy_run(即warmup和graph捕获)阶段被调用,实际推理(inference)时不会再调用,而是直接复用捕获时生成的元数据。API文档说明。
需要详细说明其在捕获和推理流程中的作用吗?
Sources:
Hint: Mention @RunLLM in the post for followups.
@RunLLM
if broadcast_pp_output:
model_output_broadcast_data = {
"logits": logits.contiguous(),
} if logits is not None else {}
model_output_broadcast_data = get_pp_group().broadcast_tensor_dict(
model_output_broadcast_data, src=len(get_pp_group().ranks) - 1)
assert model_output_broadcast_data is not None
logits = model_output_broadcast_data\["logits"\] 这段代码的作用是?
