Skip to content

feat: support async decode scheduling#1107

Open
Vinkle-hzt wants to merge 18 commits into
alibaba:mainfrom
Vinkle-hzt:feat/async_rebase
Open

feat: support async decode scheduling#1107
Vinkle-hzt wants to merge 18 commits into
alibaba:mainfrom
Vinkle-hzt:feat/async_rebase

Conversation

@Vinkle-hzt

@Vinkle-hzt Vinkle-hzt commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add opt-in async decode scheduling for normal and MTP executors, including AsyncRunner, stream device state, deferred KV release, and safer host-buffer lifetime handling
  • move hot-path model/attention metadata preparation toward device-resident tensors with fused CUDA kernels for CUDA graph input prep, MTP target verify, speculative decode metadata, and speculative sampling
  • remove generic context_total_kv_length propagation from Python/CUDA graph inputs; keep the remaining host sync local to C++ TRT V2 only
  • avoid CUDA graph prefill fused-fill D2H sync by reading the final KV cumulative length from device-side cu_kv_seqlens
  • keep PyWrappedModel embedding H2D copies async while holding host tensor lifetime correctly
  • add ROCm compatibility for the new async/MTP paths by sharing common kernels, updating ROCm attention/FusedRope paths, and fixing ROCm build runtime paths
  • add CpuTpBroadcaster for intra-node CPU TP broadcasts to avoid small tensor synchronization overheads
  • support MTP vocab prune

Tests

Added/updated coverage includes:

  • //rtp_llm/cpp/normal_engine/test:async_runner_test
  • //rtp_llm/cpp/distribute:cpu_tp_broadcaster_test
  • //rtp_llm/models_py/bindings/cuda/test:cuda_speculative_sampling_test
  • existing cache, stream, CUDA graph, MTP executor, and distributed tests updated for async/device-state behavior

Local checks:

  • pre-commit clang-format/black/isort checks passed for touched commits
  • python py_compile checks passed for updated Python CUDA graph/TRT helper files

@Vinkle-hzt Vinkle-hzt requested a review from LLLLKKKK as a code owner June 16, 2026 07:43
@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1107

Status: BLOCKING

Summary: P0/1 · P1/13 · P2/16 · P3/0

Blocking Issues

P0

  • combo_position_ids 的 H2D 拷贝会被清空导致未初始化位置输入 @ rtp_llm/cpp/models/PyWrappedModel.cc:718
    • 建议:在调用 prepareAttentionInputs() 之后再构造 PyModelInputs 的 combo_position_ids,或改成直接 .to(torch::kCUDA, true) 并确保 host 生命周期;同时移除会被清空的队列写入。

P1

  • 多模态 extra input 被错误复用同一个向量 @ rtp_llm/cpp/normal_engine/NormalModelInputGatherer.cc:513
    • 建议:恢复两个独立 vector:一个收集 multimodal_features,一个收集 mm_extra_input,分别赋给 model_input.multimodal_features/mm_extra_input。
  • MTP 异步 dispatch 下一轮未等待上轮 worker @ rtp_llm/cpp/normal_engine/speculative/MtpExecutor.cc:879
    • 建议:在 decodeStep 构造 StreamGroups/读取 GenerateStream 前同步 spec_bookkeeping_runner_;若要跳过同步,必须保证本轮 gather 的所有 StreamGroups/KV/host 状态读取都由已发布 device state 替代。
  • Normal DROP_BROAD_SYNC 路径仍读取竞态 host/KV 状态 @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:187
    • 建议:在 DROP_BROAD_SYNC 下也应先等待上一轮 worker,或把 StreamGroups 构造、KV block table gather、所有 batch/seq_len 输入都改为不读正在被 worker 修改的 host 状态。
  • 多模态 extra input 被混入 features 后丢失 @ rtp_llm/cpp/normal_engine/NormalModelInputGatherer.cc:427
    • 建议:恢复两个独立 vector,分别收集 multimodal_features 和 mm_extra_input;move 前不要复用同一个容器。
  • 调试日志格式化会把 streamId 当成字符串指针 @ rtp_llm/cpp/model_rpc/DecodeRpcServer.cc:244
    • 建议:把 %s 改为与 GenerateStream::streamId()int64_t 类型匹配的 %ld/%lld,或先转成 std::to_string(...).c_str()
  • CUDA graph 不支持的请求会直接 abort,破坏 fallback 语义 @ rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:600
    • 建议:恢复 canRun/tryGetRealGraph* 对不匹配 shape 返回 false 的行为;只对内部不变量损坏使用 CHECK。
  • 删除 prefix_lengths_d 绑定会打断现有 Python 线性注意力路径 @ rtp_llm/models_py/bindings/OpDefs.cc:114
    • 建议:恢复 prefix_lengths_d/input_lengths_d 的字段与 pybind,或一次性把所有 Python 消费方改成等价的 device tensor 属性并同步 pyi。
  • ROCm 采样返回 cum_log_probs 时形状错误 @ rtp_llm/models_py/bindings/core/CudaSampleOp.cc:646
    • 建议:按 CUDA 分支逻辑用 samples_t gather 出每个 batch 选中 token 的概率,只把 [batch_size] 的 log_prob 加到 cum_log_probs。
  • ROCm multinomial 采样忽略每请求随机种子 @ rtp_llm/models_py/bindings/core/CudaSampleOp.cc:632
    • 建议:像 CUDA 分支一样为每个 batch 使用 params.generator[i] 生成随机数,或按行调用带 generator 的采样逻辑,保证 random_seed 语义一致。
  • *移除 PyAttentionInputs 的 _lengths_d 会让现有模型路径 AttributeError @ rtp_llm/models_py/bindings/OpDefs.h:197
    • 建议:恢复 prefix_lengths_d/input_lengths_d 绑定,或同步修改所有 Python 调用点改用已确保在 device 上的 prefix_lengths/input_lengths。
  • MHA device planner 未恢复 batch/position buffer 逻辑尺寸 @ rtp_llm/models_py/bindings/cuda/FlashInferMlaParams.cc:679
    • 建议:在 fillParamsMhaDevice 后把 batch_indice_d/positions_d 至少设为 input_token_num_upper,或维护精确 nnz 并设置为真实 token 数,避免后续 narrow(nnz) 失败。
  • 带 prefix 的 paged TRT prefill 传入了错误的 KV token 总数 @ rtp_llm/cpp/models/PyWrappedModel.cc:216
    • 建议:按 sum(input_lengths + prefix_lengths) 填充 context_total_kv_length;可在 prefix 全零时用 total_tokens,否则从 cu_kv_seqlens[-1] 同步/计算出正确标量后再传给 TRT。
  • CUDA graph 不支持当前 batch 时会抛异常而不是回退 @ rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:618
    • 建议:把 canRun 子路径中的 unsupported 条件改为日志+return false,仅对内部不变量损坏保留 CHECK;确保 PyWrappedModel 能走普通 forward 回退。

Non-blocking Suggestions

P2

  • decode_cu_seqlens_host 在 stub 中被重复声明为只读属性 @ rtp_llm/ops/librtp_compute_ops/__init__.pyi:216
    • 建议:删除新增的只读 @property,或按 def_readwrite 语义补充 setter,避免类型层面把可写 pybind 字段收窄为只读。
  • decode device-state 路径存在 per-stream 小 CUDA op @ rtp_llm/cpp/normal_engine/NormalModelInputGatherer.cc:390
    • 建议:把 next_seq_len_gpu 先批量 cat,再做一次 batch 级减 1/类型转换;或在发布阶段保存批量 state,避免每个 stream 一次小 kernel/临时 tensor。
  • forceSpAccept 对 CUDA mask 逐元素 CPU 写 @ rtp_llm/cpp/normal_engine/speculative/SpeculativeSampler.cc:148
    • 建议:在 CPU pinned tensor 上构造完整 mask 后一次 H2D,或收集 forced indices 后用一次 index_fill_/scatter kernel 设置。
  • 异步 dispatch 失败只打日志不向调用方传播 @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:643
    • 建议:将 worker 返回状态保存到 AsyncRunner 或转成异常,在下一次 sync/launch 时传播;至少保持 async path 与同步 dispatch 的错误语义一致。
  • CUDA graph prefill 准备阶段仍有设备到主机同步 @ rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:307
    • 建议:避免在 prepareAttentionInputs 热路径对 CUDA prefix_lengths 调用 .item();优先复用已生成的 cu_kv_seqlens/host mirror,或在 fused prepare kernel 内填充 tail 值。
  • RoPE prefill 参数准备保留同步式 max 读取 @ rtp_llm/ops/fused_rope_kvcache_op.py:68
    • 建议:将 max_seq_len/max_prefix_length 从上游元数据传入,或使用已有 cu_seqlens/cu_kv_seqlens 计算,避免每次 prepare 对 CUDA length tensor 触发同步。
  • Hybrid incrMalloc 成功路径复制完整 block 快照 @ rtp_llm/cpp/cache/HybridTypeKVCacheAllocator.cc:149
    • 建议:改为按实际修改的 BlockIds 做延迟快照,或让 LinearKVCacheGroup 返回本次新增/backfill blocks 用于失败回滚,避免 decode 分配成功路径 O(batchgroupblocks) 拷贝。
  • 新增的 shouldMaterializeBlock 只有声明没有定义 @ rtp_llm/cpp/cache/LinearKVCacheGroup.h:36
    • 建议:如果需要复用 materialize 逻辑就补上定义并让 malloc/getNeedBlocks 共用;否则删除该未实现声明,避免后续调用产生链接错误。
  • Decode RPC debug 日志格式符与 streamId 类型不匹配 @ rtp_llm/cpp/model_rpc/DecodeRpcServer.cc:243
    • 建议:把格式符改为 %ld,或先用 std::to_string(streamId()) 后传 c_str()。
  • MLA 参数准备会同步回读长度张量 @ rtp_llm/models_py/bindings/cuda/FlashInferMlaParams.cc:21
    • 建议:为 MLA fillParams 增加 device-only 路径,或在上游保留 pinned CPU mirror,避免每层 prepare 触发 D2H sync。
  • TRT prefill 准备路径仍有 GPU 同步标量回读 @ rtp_llm/models_py/bindings/cuda/TRTAttnOp.cc:47
    • 建议:把 max_len/has_prefix 在 metadata 构建阶段传入,或用 device kernel 写入参数,避免 prepare/support 中 item() 同步。
  • 采样热路径重新引入 Python/GIL 调用 @ rtp_llm/models_py/bindings/core/CudaSampleOp.cc:44
    • 建议:保留 C++/CUDA renorm kernel 或封装 C++ FlashInfer API,并复用临时 workspace,避免 per-token Python 调用和分配。
  • attention metadata kernel 单线程串行填充 padding_offset @ rtp_llm/models_py/bindings/cuda/kernels/attention_input_metadata.cu:24
    • 建议:用并行 scan/fill 拆分实现;至少让 padding_offset 按 token 并行写,避免长 prefill 下单线程 O(total_tokens)。
  • forward 热路径重复构建 attention inputs @ rtp_llm/cpp/models/PyWrappedModel.cc:724
    • 建议:删除 forward() 中未使用的本地 attention_inputs 构建,统一由 prepareAttentionInputs()/attention_inputs_ 提供,避免重复分配、H2D/D2H 和 metadata kernel。
  • fill_params_mha_device 缺少 pyi 声明 @ rtp_llm/ops/librtp_compute_ops/rtp_llm_ops.pyi:12
    • 建议:在 librtp_compute_ops/rtp_llm_ops.pyi 和外层 rtp_llm/ops/rtp_llm_ops.pyi 同步补充 fill_params_mha_device 的完整签名。
  • FlashInfer decode replay 会静默截断页表 @ rtp_llm/models_py/bindings/cuda/kernels/cuda_graph_prepare.cu:51
    • 建议:在 host 侧用 graph/capture 最大 seq blocks 校验 block table 宽度,或在 kernel 写 error flag 并在 replay 前 fail-fast,避免 FlashInfer 读取不完整页表。

Checklist ✅ (56 items passed)

Strengths

  • 新增的 XQA/FlashInfer pyi 方法能在对应 pybind 注册中找到实现。
  • MTP d2t/t2d 权重名与 C++ W.h 中的常量保持一致。
  • 本分片的两个 .pyi 变更只补齐绑定声明,不引入运行时开销。
  • 新增 MTP vocab 映射使用 sp_id,不会在 TP 切分阶段产生额外切片或拼接分配。
  • 新增 MTP d2t/t2d map 进入 TP strategy 且使用 sp_id,避免 token 映射表被 TP 切分。
  • XQA/FlashInfer 的新增 CUDA graph 更新接口已在 pyi 中暴露,便于调用侧获得静态提示。
  • AsyncRunner 将异常延迟到下一次 launch/sync 抛出,避免 worker 线程静默吞错。
  • 模型输入核心张量发布到 CUDA 后再进入 PyWrappedModel,减少隐式 CPU 同步点。
  • 新增的 pinned host holder 明确延长异步 H2D/D2H 源 tensor 生命周期。
  • AsyncRunner 对 worker 异常做了捕获并在 sync/launch 重新抛出,避免后台线程异常直接终止进程。

@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1107

Status: BLOCKING

Summary: P0/2 · P1/14 · P2/22 · P3/0

Blocking Issues

P0

  • 采样拒绝时会误走 all_same 快捷路径 @ rtp_llm/models_py/bindings/cuda/kernels/speculative_sampling/sampling.cu:271
    • 建议:将“全 token 相同可直接返回”和“采样拒绝需 q-p 重采样”拆成独立状态;do_sample=true 的拒绝分支必须进入 residual 重采样,并同步修复 ROCm kernel、补 do_sample=true 拒绝用例。
  • 拒绝采样首个 token 被拒时误走 all_same 快捷路径 @ rtp_llm/models_py/bindings/cuda/kernels/speculative_sampling/sampling.cu:255
    • 建议:在拒绝分支前同步更新 all_same_token=false,或把 all_same_token 初始化/判定改为仅当所有 speculative token 都 accepted 且 same_token 时才走 bonus 快捷路径,并补充首 token rejected 的单测。

P1

  • CP 混合 batch 下 shuffle_indices 写越界 @ rtp_llm/cpp/models/context_parallel/ContextParallelProcessorBase.cc:89
    • 建议:在 CP handleInputs 开头显式拒绝 mixed prefill+decode,或为 shuffle_indices 使用独立的 prefill offset,不要复用包含 decode token 的 input_token_idx。
  • prepareAttentionInputs 失败后会复用半成品输入 @ rtp_llm/cpp/models/PyWrappedModel.cc:627
    • 建议:把 prepared_attention_inputs_ 置 true 移到 fusedCopy 和 graph prepare 成功之后,或在 prepareAttentionInputs 内加 scope guard 失败时重置 false。
  • CUDA graph canRun 失去回退能力 @ rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:652
    • 建议:canRun 应保持判定函数语义:超出 capture range、空 range、hybrid group 不匹配时记录告警并 return false,让 PyWrappedModel 走 normal forward;硬失败应放到初始化配置校验阶段。
  • CPU TP 广播请求期可能无限阻塞 @ rtp_llm/cpp/distribute/CpuTpBroadcaster.cc:412
    • 建议:请求期 broadcast 使用带超时的 read/write,超时后关闭连接并显式报错或降级到原 NCCL 路径,避免单个 rank 卡死拖挂整个 TP 组。
  • FA2 decode CUDA graph 回放跳过必要 replan @ rtp_llm/models_py/modules/factory/attention/cuda_impl/py_flashinfer_mha.py:779
    • 建议:FA2/tensor-core decode 回放不要在 fill_decode 后直接 return;需同步刷新 host params 并调用 _plan_decode_wrapper,或仅非 FA2 走 device-only 早退。
  • CUDA graph 不支持请求会中断而不是回退 @ rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:634
    • 建议:canRun 的 shape/capability mismatch 保持返回 false;仅内部状态损坏使用 CHECK,避免线上超出 capture range 的请求直接失败。
  • 默认 MTP decode 额外复制整份 draft all_probs @ rtp_llm/cpp/normal_engine/speculative/MtpExecutor.cc:1803
    • 建议:不要 clone 整份 draft_all_probs_full;直接保存 narrow 视图让 Tensor refcount 保持 storage 生命周期,或仅在真正启用异步/device-state 路径时发布该状态。
  • normal DROP_BROAD_SYNC 判定漏掉 position-id fallback @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:190
    • 建议:把 need_cal_position_id 条件纳入 gatherCanUseDeviceState,或在 position-id fallback 前先 sync 并重建 StreamGroups。
  • MTP stream-async 默认路径缺少上一轮 worker 同步 @ rtp_llm/cpp/normal_engine/speculative/MtpExecutor.cc:901
    • 建议:在 decodeStep 开头、读取 streams 前,对 useStreamAsync() && !useDropBroadSync() 调用 spec_bookkeeping_runner_.sync,并重建 StreamGroups。
  • CUDA graph 超出 capture range 时不能回退 normal forward @ rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:632
    • 建议:将超出 capture_range_ 的正常动态请求返回 false;仅对配置空、内部状态不一致等真正 invariant violation 保留硬错误,并覆盖 prefill/decode fallback 测试。
  • FA2 decode CUDA graph replay 跳过必要 replan @ rtp_llm/models_py/modules/factory/attention/cuda_impl/py_flashinfer_mha.py:779
    • 建议:fill_decode_cuda_graph_params 后若 _requires_fa2_cuda_graph_replan() 为 true,同步刷新 host page_indptr/page_indices/last_page_len 并调用 _plan_decode_wrapper,或禁止该 backend 使用当前 graph replay 快路径。
  • CPU TP 广播请求期可能无限阻塞 @ rtp_llm/cpp/distribute/CpuTpBroadcaster.cc:410
    • 建议:为 broadcast 阶段也使用带 deadline 的 read/write,并在失败时 reset broadcaster、返回明确错误;必要时允许退回 NCCL 路径或触发进程级失败。
  • AsyncRunner::launch 抛错会泄漏 pending bookkeeping 计数 @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:616
    • 建议:用 RAII 包住 inc 后到 launch 成功之间的窗口;launch 抛出时立即对已 inc 的 streams 执行 decPendingAsyncBookkeepingAndMaybeRelease。MtpExecutor 的同类路径也要同步修复。
  • 非 MLA CP prefill 仍可能读取未填充的 host block table @ rtp_llm/cpp/models/PyWrappedModel.cc:303
    • 建议:enable_prefill_cp 且 CP 实现仍需要 host table 时,也为 group0 materialize pinned host mirror;更好是改 CP prefill 使用 device planner,移除 host 依赖。

Non-blocking Suggestions

P2

  • RoPE prefill 参数准备仍会触发 GPU 同步 @ rtp_llm/ops/fused_rope_kvcache_op.py:67
    • 建议:复用调度/metadata 阶段已计算的 max_seq_len/max_prefix_length,或在 device prepare kernel 中维护,避免 Python .item() 同步。
  • Aiter support 判定会同步读取 prefix_lengths @ rtp_llm/models_py/modules/factory/attention/rocm_impl/aiter.py:721
    • 建议:在 PyAttentionInputs 中携带 host 侧 has_prefix 标志,或复用调度侧 prefix 元数据,避免 support() 对 device tensor 做 max().item()。
  • CUDA graph replay 元数据构造单线程串行 @ rtp_llm/models_py/bindings/cuda/kernels/cuda_graph_prepare.cu:112
    • 建议:按 batch/page 并行填充 page_indice,并用并行 scan 或复用已有 MHA planner 生成 indptr,避免 decode replay 热路径被单线程 GPU loop 限制。
  • attention metadata kernel 串行填充 padding_offset @ rtp_llm/models_py/bindings/cuda/kernels/attention_input_metadata.cu:24
    • 建议:保留小规模 fast path,但对 total_tokens 较大场景拆成并行 prefix-sum + 并行填充,或至少按阈值走并行 kernel。
  • ROCm 采样按 batch 逐行调用 multinomial @ rtp_llm/models_py/bindings/core/CudaSampleOp.cc:597
    • 建议:无 per-request generator 时保持 batched multinomial;有 generator 时考虑预生成随机数后走一次向量化采样 kernel,避免 B 次 kernel/分配再 cat。
  • MHA 设备 planner 的输出容量校验偏弱 @ rtp_llm/models_py/bindings/cuda/kernels/mha_paged_attn_plan.cu:159
    • 建议:在 launch 前校验 seq_size_per_block > 0,并保证 sum(input_lengths) 不超过 batch_indice/positions 容量、每行页数不超过 max_blocks_per_bs。
  • attention metadata 输出张量缺少容量校验 @ rtp_llm/models_py/bindings/cuda/kernels/attention_input_metadata.cu:103
    • 建议:补充 cu_seqlens、cu_kv_seqlens 的 numel >= batch_size + 1 校验,并校验 padding_offset 为 contiguous int32。
  • ROCm rejection sampling 未检查 kernel launch 失败 @ rtp_llm/models_py/bindings/rocm/speculative_sampling/sampling.cu:632
    • 建议:launch 后返回 hipGetLastError(),必要时加 ROCM_CHECK_ERROR/同步调试路径,避免 launch 配置错误被吞掉。
  • cache store 准备阶段存在未使用的 D2H 拷贝 @ rtp_llm/cpp/models/PyWrappedModel.cc:404
    • 建议:删除这两个未使用的 host 拷贝,或真正把它们传给 WriteCacheStoreOp,避免 pd_separation 路径每步额外 D2H 和 pinned 分配。
  • micro-batch 路径重复同步 input_lengths 到 CPU @ rtp_llm/cpp/models/PyWrappedModel.cc:1011
    • 建议:在 forwardMicroBatched 中生成一次 input_lengths 的 host mirror,并传给 planMicroBatches/splitInputsIntoMicroBatches 复用,避免同一 forward 两次阻塞 D2H 和 pin_memory。
  • micro-batch 已在 CUDA 的 token 仍被 clone @ rtp_llm/cpp/models/PyWrappedModel.cc:479
    • 建议:当 micro_inputs.combo_tokens 已是 CUDA tensor 时直接复用;只有确实需要隔离或 CPU 输入时再 clone/to CUDA。
  • EPLB 配置锁内快照未被使用 @ rtp_llm/cpp/models/eplb/ExpertBalancer.cc:131
    • 建议:改为使用 cur_data.toList(),或把 toList 与 buffer 填充放在同一把锁保护下。
  • Hybrid incrMalloc 成功路径复制完整 block 快照 @ rtp_llm/cpp/cache/HybridTypeKVCacheAllocator.cc:149
    • 建议:只记录本次 malloc/backfill 修改过的位置和旧值,失败时按 diff 回滚,避免每次 decode 都 O(batchgroupblocks) 拷贝。
  • 线性 KV 增量分配每步扫描全部历史 block @ rtp_llm/cpp/cache/LinearKVCacheGroup.cc:105
    • 建议:只检查本步可能从 NULL 变为需物化的少量位置,或维护待 backfill 索引,避免长上下文 decode 累积为 O(N^2)。
  • CUDA graph decode 尾部清零重复发起 fill kernel @ rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:498
    • 建议:CUDA fused prepare fill 已覆盖 decode tail 时删除后续 aten fill_;仅非 CUDA fallback 保留单独 fill。
  • ROCm prefill CUDA graph 准备仍有设备同步读取 @ rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:386
    • 建议:为 ROCm/HIP 也使用 host mirror 或设备端 fill/from-device-value kernel,避免 replay prepare 的 .item() 同步。
  • Normal decode 默认路径引入每流小 CUDA op @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:592
    • 建议:把 last_sample_token/next_seq_len 按 batch 一次性生成 tensor,再给 stream 保存 view;或只在 RTP_LLM_STREAM_ASYNC / DROP_BROAD_SYNC 需要时发布 device state。
  • forceSpAccept 掩码有不必要 CUDA 分配和标量写 @ rtp_llm/cpp/normal_engine/speculative/SpeculativeSampler.cc:148
    • 建议:先用 CPU bool/vector 扫描是否存在 force;只有 has_force 时一次性构造 pinned CPU mask 并 H2D,或把 forced stream 索引传给 GPU kernel 批量处理。
  • 异步 dispatch 失败只打日志不向调用方传播 @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:643
    • 建议:保存 worker status,并在下一次 sync/launch 传播;或对相关 stream reportError,避免静默成功。
  • MHA device planner 未校验页数不超过 block table 列数 @ rtp_llm/models_py/bindings/cuda/kernels/mha_paged_attn_plan.cu:90
    • 建议:在 kernel 或调用前校验每个请求所需页数不超过 kv_cache_block_id.size(1);超出时 fail fast,避免 silent truncation/OOB,并补充边界测试。
  • attention metadata 构造串行填充 padding_offset @ rtp_llm/models_py/bindings/cuda/kernels/attention_input_metadata.cu:17
    • 建议:将 cu_seqlens 前缀和与 padding_offset 生成拆成并行 scan/fill,或按 total_tokens/batch_size 设置阈值选择并行路径,并加 perf case 覆盖长 prefill。
  • FlashInfer decode graph replay 元数据构造单线程串行 @ rtp_llm/models_py/bindings/cuda/kernels/cuda_graph_prepare.cu:112
    • 建议:对 page_indice 拷贝并行化,并用并行 prefix-sum 生成 indptr;至少为 graph replay batch/page 数加 guard,超过阈值回退非 graph 或并行 planner。

Checklist ✅ (56 items passed)

Strengths

  • CUDA graph 路径改为直接读取 device-resident length tensor,避免回放前依赖 host 镜像同步。
  • Aiter paged prefill 为 sanitized block table 增加固定地址缓冲,并在容量超出 capture 时显式失败,降低静默越界风险。
  • Fused RoPE decode 对 sequence_lengths 增加 CUDA tensor 断言,错误输入会更早暴露。
  • CUDA graph replay 路径改为直接消费 device 侧 length/block table,减少 host mirror 过期和额外 H2D 依赖。
  • XQA 新增 update/update_kv_cache_offset 原地刷新路径,避免 replay 时回退到完整 prepare 带来的对象重建。
  • Aiter paged prefill 为 sanitized block table 增加固定地址 buffer 和容量检查,提升 graph 地址稳定性。
  • CUDA graph replay 路径改为使用 device 侧 length/block table,避免依赖已移除的 *_d 别名。
  • XQA replay 同时更新 FMHA 参数和 RoPE kv_cache_offset,且保留旧实现 fallback,兼容性较稳。
  • 新增 MTP metadata prepare kernel,把多处小 tensor 元数据更新从 host 路径合并到 device 侧。
  • FlashInfer MHA device planner减少 steady-state decode 的 host metadata loop 和 H2D copy。

@LLLLKKKK

Copy link
Copy Markdown
Collaborator

AI Code Review - PR #1107

Status: BLOCKING

Summary: P0/1 · P1/4 · P2/44 · P3/12

Blocking Issues

P0

  • RejectionSamplingParams 在 OpData.h 中重复定义导致编译错误 @ rtp_llm/models_py/bindings/core/OpData.h:393
    • 建议:删除 OpData.h 中重复的 RejectionSamplingParams 定义(保留 lines 374-383,删除 lines 393-402)

P1

  • ROCm 回退路径中 .item() 引入 D2H 同步阻塞 @ rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:386
    • 建议:ROCm 路径也应通过 device-side fill kernel 或 device memcpy 读取 cu_kv_seqlens 值,避免 .item() 阻塞 GPU pipeline。可实现类似 CUDA 侧的 fused fill from device value 功能,或者用 hipMemcpyAsync D2D 拷贝该值。
  • 指标除零:avg_accept_num 为 0 时 sp_estimate_tpot_us 产生 Inf @ rtp_llm/cpp/metrics/RtpLLMMetrics.cc:320
    • 建议:在 avg_accept_num 作为除数前加守卫:if (avg_accept_num > 0) { ... }。或者将外层 guard 条件改为 total_accepted_token_num > 0。同一分支第 319 行 sp_avg_fix_accept_rate_metric 的 (avg_accept_num - 1) / spec_steps 在 avg_accept_num < 1 时也会产生负值指标。
  • 测试缺少 kv_cache_kernel_block_id_device,prepare() 调用将 CHECK-fail 崩溃 @ rtp_llm/models_py/modules/factory/attention/cuda_impl/test/test_flashinfer_prefill/test_py_flashinfer_paged_prefill_cuda_graph.py:73
    • 建议:在 _make_inputs 第 73 行后添加 inp.kv_cache_kernel_block_id_device = block_ids.to('cuda'),与其他测试文件保持一致。
  • CUDA sampleGreedy topk=1 快速路径遗漏 cum_log_probs 检查,与 ROCm 不一致 @ rtp_llm/models_py/bindings/core/CudaSampleOp.cc:354
    • 建议:CUDA sampleGreedy 快速路径条件增加 && !params.cum_log_probs.has_value(),与 ROCm line 542 保持一致

Non-blocking Suggestions

P2

  • MLA 路径 setupKVCacheForAttentionInputs 同步 D2H 拷贝阻塞流水线 @ rtp_llm/cpp/models/PyWrappedModel.cc:304
    • 建议:MLA 路径的 .cpu() 是阻塞 D2H(cudaMemcpy sync),会打断 async prepare 流水线。建议改为 torch::empty(pinned) + copy_(non_blocking=true),并在实际消费前插 event 同步。当前标注了 TODO(async) 但未用 async 拷贝。这里不是 P1 因为只影响 MLA 模型且 block table 通常很小,但在高 batch decode 场景下会成为瓶颈。
  • planMicroBatches 和 splitInputsIntoMicroBatches 各自独立做一次同步 D2H @ rtp_llm/cpp/models/PyWrappedModel.cc:1011
    • 建议:planMicroBatches 和 splitInputsIntoMicroBatches 在 forwardMicroBatched 中顺序调用,各自做一次 .cpu() 同步 D2H。建议在 forwardMicroBatched 入口统一做一次 host 镜像,传给两个函数复用,避免重复的阻塞拷贝。
  • length_plus_one_device 产生不必要的临时 tensor 和类型转换 @ rtp_llm/cpp/models/PyWrappedModel.cc:120
    • 建议:(tensor_d + 1) 返回 kInt32 但外层又调了 .to(torch::kInt32),这是冗余的类型转换(虽然 PyTorch 在类型相同时会短路,但仍有 dispatch 开销)。此外 tensor_d + 1 在 GPU 上分配了一个临时 tensor 再做 element-wise add,对于小 tensor 来说 kernel launch overhead 比计算本身大。建议去掉冗余的 .to(torch::kInt32),或将 +1 合并到上游 CUDA kernel 中。
  • ContextParallelProcessorBase 中 combo_tokens D2H 拷贝后未持有引用即做 async H2D @ rtp_llm/cpp/models/context_parallel/ContextParallelProcessorBase.cc:22
    • 建议:CP 处理中先 .cpu().pin_memory()(阻塞 D2H + pin + alloc),然后最终用 .to(kCUDA, non_blocking=true) 上传。但 cp_split_input_tokens 本身是 pinned host tensor,non_blocking 上传是安全的。问题是前面的 .cpu() 是全同步的阻塞拷贝。combo_tokens 可能很大(prefill token 数 × 4 bytes)。考虑用 event-based async D2H 或者直接在 GPU 上做 CP split(用 gather kernel)来消除阻塞。标为 P2 因为 CP 只在 prefill 且 tp_size > 1 时激活。
  • tpSyncModelInputs 中 mm_extra_input 仍使用旧式 execBroadcast + cudaSyncAndCheck @ rtp_llm/cpp/models/ModelTypes.cc:140
    • 建议:PR 已将 shape_hints 和 mm_features_shape 的广播改为 execBroadcastCpu(避免 cudaSyncAndCheck),但 mm_extra_input_shape 仍使用旧式 execBroadcast + execSyncCommunication + cudaSyncAndCheck,导致一次额外的 GPU 同步。建议统一改为 execBroadcastCpu,与其他 shape broadcast 路径一致。
  • prepareWriteCacheParams 中异步拷贝的 host 张量未被使用 @ rtp_llm/cpp/models/PyWrappedModel.cc:404
    • 建议:input_lengths_host 和 prefix_lengths_host 被计算但从未传入 PyCacheStoreInputs 或任何后续逻辑。要么将它们传给 cache store(如果下游需要 host 长度),要么删除这两行以避免浪费 pinned memory 分配和无用的 async D2H 拷贝。
  • updateKVCacheKernelBlockId 未更新 MLA 模型的 host block table @ rtp_llm/cpp/models/PyWrappedModel.cc:657
    • 建议:updateKVCacheKernelBlockId 只刷新了 device views,但对 MLA 模型 setupKVCacheForAttentionInputs 会设置 kv_cache_kernel_block_id_host(被 SparseMlaParams 和 cuda_graph_runner H2H 拷贝消费)。若 MTP propose+verify 后调用此方法,MLA 的 host block table 会过时。建议在 use_mla 路径下同步刷新 host copy,或在跨 shard 中确认 CUDA graph prepareAttentionInputs 已提前完成了 host copy。标记为 P2 因为需要跨文件确认影响范围。
  • ContextParallelProcessorBase async H2D 缺少 host 张量生命周期保证 @ rtp_llm/cpp/models/context_parallel/ContextParallelProcessorBase.cc:96
    • 建议:cp_split_input_tokens、input_lengths 等 pinned host 局部变量在 handleInputs 返回后立即析构。虽然 PyTorch caching allocator 的 event 机制通常保护 pinned memory 不被立即重用,但这依赖内部实现细节且难以推理。建议通过 model_input 的某个 holder 显式持有这些 host 张量(类似 PyWrappedModel 中 buffer_holder_ 的做法),或者加注释说明 PyTorch pinned allocator 的安全保证。
  • pinned_host_i32 lambda 对 CUDA 张量做同步 D2H @ rtp_llm/cpp/models/PyWrappedModel.cc:132
    • 建议:pinned_host_i32 中 .cpu() 是同步阻塞的 D2H 拷贝,在 async 路径中会引入 GPU pipeline bubble。虽然有 TODO(async) 注释,但这与 PR 整体目标(消除阻塞拷贝)矛盾。建议改为 async copy 到 pinned host(类似 prepareWriteCacheParams 中的 async_to_pinned_host 模式),或确保此路径只在非关键 path 调用。
  • dispatchOutputAsync 错误被静默丢弃,仅做日志记录 @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:643
    • 建议:dispatch worker 中 status 错误仅有 LOG_ERROR。若 dispatch 失败(如 stream update 异常),错误无法传递到调用方,推荐将错误存储到成员变量,在下次 sync() 时向上层传播,或者利用 AsyncRunner 已有的 exception 传播机制,改为 throw。
  • asyncDebugEnabled() 每次调用都执行 getenv,热路径重复系统调用 @ rtp_llm/cpp/normal_engine/NormalOutputDispatcher.cc:17
    • 建议:与其他 env flag 一样使用 static const bool enabled = [](){...}() 模式缓存结果,避免热路径上每次调 getenv。NormalModelInputGatherer.cc:16 同理。
  • processDecodeStreams 中 use_normal_device_state 路径缺少 vocab_size 校验 @ rtp_llm/cpp/normal_engine/NormalModelInputGatherer.cc:389
    • 建议:device-state 路径跳过了 currentTokens[0] >= ctx.input_vocab_size 检查。虽然 sampler 输出应在 vocab 范围内,但如果 state 被错误填充(如 grpc 传入),可能导致后续 embedding lookup 越界。建议至少在 debug/check 模式下加一个 D2H 检查,或补充防御性注释说明依赖链。
  • gatherKvCacheKernelBlockId 中 kernel_blocks.size() 未做边界检查 @ rtp_llm/cpp/normal_engine/NormalModelInputGatherer.cc:557
    • 建议:添加 RTP_LLM_CHECK(kernel_blocks.size() <= per_batch_stride) 断言,防止某 stream 的 block 数超出 max_blocks_num 导致越界写入 pinned memory。
  • per-stream torch::cat 在 decode 热路径产生 O(N) kernel launch 开销 @ rtp_llm/cpp/normal_engine/NormalModelInputGatherer.cc:416
    • 建议:device-state 路径下,每个 stream 产生一个 reshape+push_back,最后 torch::cat + .to(kInt32)。当 batch 大时产生 N 次 reshape kernel + 1 次 cat + 1 次 cast。建议预分配 [batch] 的 CUDA tensor,用 narrow()+copy_ 或 torch::stack 替代 per-stream push + cat,减少 kernel launch 次数。sequence_lengths 同样在 :390 每个 stream 做 (gpu-1).to(kInt32).reshape 是独立 kernel,可以用向量化减法替代。
  • publishNormalDeviceState 每个 stream 循环内发起多次 GPU kernel @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:566
    • 建议:per-stream 循环内每个 stream 做 narrow+to(kInt32) + (gpu+1).to(kInt32),batch=128 时是 256+ 次 kernel launch。建议把整个 batch 的 token_ids 一次 slice+cast,next_seq_len 一次向量加法,再 narrow 出 per-stream view(narrow 仅是 metadata,不触发 kernel)。dispatchDecodeAsync 中的 publishSyncMtpDeviceState 已经采用了更好的 batch 模式。
  • MtpExecutor::draftModelDecode 中 per-stream cat+gather 可合并 @ rtp_llm/cpp/normal_engine/speculative/MtpExecutor.cc:1577
    • 建议:per-stream reshape+push_back 再 torch::cat 的模式在 hot decode path 上产生 2*N 次 reshape kernel + 2 次 cat。accept_tokens/accept_len 每个 stream 形状已知且固定([1, propose_step+1] / [1]),考虑预分配 batch tensor 然后 narrow+copy_,或者直接用 torch::stack。这是 decode-only 热路径,batch 越大影响越明显。
  • MtpBatchStreamProcessor 多处 per-stream cat 同质模式 @ rtp_llm/cpp/normal_engine/speculative/MtpBatchStreamProcessor.cc:523
    • 建议:gatherMtpDecodeModelInputFromDeviceState、prepareDecodeDraftModelInput、prepareOneStepSpecDecodeModelInput 均采用 per-stream push_back + torch::cat 模式。对于固定形状的标量 tensor,改用预分配 batch tensor + narrow 写入可减少 kernel launch 和内存分配次数。影响范围:decode 热路径。
  • dispatchDecodeAsync 构建 prev_seq_len_all 时 per-stream torch::tensor 创建 @ rtp_llm/cpp/normal_engine/speculative/MtpExecutor.cc:1937
    • 建议:fallback 分支每个 stream 调用 torch::tensor({val}, cuda_i32) 在 GPU 上创建一个标量 tensor,然后 cat。每次调用都触发 cudaMalloc + H2D copy。建议改用 pinned CPU tensor 写入 batch 值后一次 H2D,或直接预分配 batch CUDA tensor。
  • draft_probs_all.clone() 在 hot path 上复制完整 vocab 概率矩阵 @ rtp_llm/cpp/normal_engine/speculative/MtpExecutor.cc:1803
    • 建议:clone() 复制整个 [batch*(propose_step+1), vocab_size] 的 float 概率矩阵,vocab=****** + batch=128 时约 70GB/s 带宽负载。如果下游只需要 per-stream narrow view,且生产者不会 mutate 原 tensor,可以直接用 narrow view 避免 clone。如果 clone 是为了防止 dispatch worker 和下次 forward 竞争,可考虑用 event 保护而非全量拷贝。
  • SpeculativeSampler::batchSample 中 per-stream generator 循环破坏 batch 连续性 @ rtp_llm/cpp/normal_engine/speculative/SpeculativeSampler.cc:81
    • 建议:当多个 stream 有自定义 generator 时,每次 torch::rand 是独立 kernel launch。这在 large batch deterministic 场景下可能影响性能。如果 seeded stream 占比低可以接受,但建议加注释说明 trade-off,或者在全部 seeded 时用 batch generator。
  • dispatchDecodeAsync 中 next_real_seq_len 过度估计 @ rtp_llm/cpp/normal_engine/speculative/MtpExecutor.cc:1998
    • 建议:next_real_seq_len 被 GenerateStateMachine::handleRunning 用于 incrKVBlock 提前分配。当前使用 propose_step_+1(最大值)是安全的——多分配的 block 后续会回收——但若 propose_step 较大(如 7),每轮每 stream 可能浪费一个 KV block。如果 accept_len 已是 CUDA tensor,可用 GPU 上的 prev_seq_len + accept_len 替代,减少 block 浪费。标记为 P2 因功能正确但有性能影响。
  • publishSyncMtpDeviceState 中 next_real_seq_len 未包含 accept_len @ rtp_llm/cpp/normal_engine/speculative/MtpExecutor.cc:1824
    • 建议:sync 路径中,specUpdate 已更新了 host seqLength(),但 next_real_seq_len 设为 last_real_seq_len(dispatch 前的值),意味着下一轮 scheduler 若读此值做 incrKVBlock 会少分配。不过 sync 路径不存在 race,scheduler 会直接读 live seqLength(),此处值不会被使用。建议保持一致性设为 stream->seqLength() 而非旧值。
  • SpeculativeSampler::draft_probs_padding_buffer_ 作为 mutable 成员缺少线程安全 @ rtp_llm/cpp/normal_engine/speculative/SpeculativeSampler.h:72
    • 建议:当前架构下 SpeculativeSampler::forward 只在主线程调用,不存在并发问题。但 buffer_holder_ 和 draft_probs_padding_buffer_ 均为 mutable 成员且在 const 方法中修改,如果未来有多线程使用场景,应加注释说明线程约束。标记为 P2(当前安全,但代码可读性/维护性问题)。
  • ROCm 采样逐行 multinomial 导致 batch_size 次独立 GPU kernel launch @ rtp_llm/models_py/bindings/core/CudaSampleOp.cc:626
    • 建议:逐行 multinomial 会产生 batch_size 次独立 kernel launch + 最终 cat,对于大 batch 开销显著。可以先将不需要自定义 generator 的行批量调用 multinomial,仅对有自定义 generator 的行逐个采样,减少总 launch 次数。不过这是 ROCm workaround 路径(FlashInfer 采样在 ROCm TP>1 下 crash),功能正确性优先,perf 改进可作为 follow-up。
  • buildAttentionInputMetadataKernel 单线程串行计算 padding_offset @ rtp_llm/models_py/bindings/cuda/kernels/attention_input_metadata.cu:24
    • 建议:该 kernel 以 <<<1,1>>> 启动,所有工作由单线程完成,包括 O(total_tokens) 的 padding_offset 填充。对于大 context batch (token 数上千),这会成为瓶颈。建议先单线程计算 cu_seqlens/cu_kv_seqlens(batch_size 量级),再用并行 kernel 填充 padding_offset。不过目前 batch 规模通常较小,且替代了 host 端计算+D2H sync,实际影响有限。
  • SparseMlaParams::refreshBuffer 全量拷贝预分配 buffer 而非仅填充部分 @ rtp_llm/models_py/bindings/cuda/SparseMlaParams.cc:155
    • 建议:buf_h_i32_ 按 MIN_CACHE_INPUT_TOKEN=1024 预分配,但实际填充数据可能远小于此。可以像 FlashInferMlaAttnParams::refreshBuffer 一样只拷贝实际填充的字节数(根据 token_num),减少不必要的 H2D 传输带宽浪费。不过数据量较小(int32 级别),实际影响不大。
  • FlashInferMlaAttnParams::refreshBuffer 每次全量拷贝整个 buf_h 到 buf_d @ rtp_llm/models_py/bindings/cuda/FlashInferMlaParams.cc:391
    • 建议:预分配 buffer 按 MIN_CACHE_PAGE_NUM=1M 等最小值分配,但实际 page_num/batch_size 可能远小于此。每次 fillParams 全量拷贝整个连续 buffer(包含大量未使用空间)。可以计算实际填充数据的末尾偏移量,只拷贝 [0, actual_end) 范围。不过这是 pinned memory H2D async,不阻塞 GPU compute,影响有限。
  • TRT context_total_kv_length 读取触发 D2H 同步 @ rtp_llm/models_py/bindings/cuda/TRTAttnOp.cc:49
    • 建议:与同函数 47-48 行的 .item() 调用一致,不是新引入的同步。但如果后续要消除 prepare() 中的 D2H 同步,可以考虑把 context_total_kv_length 改为 device tensor 传给 TRT runner(需要 TRT runner 支持)。当前不阻塞。
  • XQA CUDA graph replay 热路径上每次 getattr 动态查找 @ rtp_llm/models_py/modules/factory/attention/cuda_impl/xqa.py:112
    • 建议:在 init 中一次性检查并缓存 self._has_update = callable(getattr(self.fmha_impl, 'update', None)) 和 self._has_update_offset,避免每次 decode step 的 prepare_cuda_graph 调用时重复做 getattr + callable 查找。虽然单次开销小(~微秒级),但 decode 每 step 每 layer 都调用,属于可消除的热路径开销。
  • XQA 新路径 fallback 分支仍调用 rope_kvcache_impl.prepare() 产生临时分配 @ rtp_llm/models_py/modules/factory/attention/cuda_impl/xqa.py:131
    • 建议:这个 fallback 路径在 fmha_impl 有 update 但没有 update_kv_cache_offset 时触发,会调用 rope_kvcache_impl.prepare() 创建完整的 FusedRopeAttnParams(包含 convert_offset_to_block_array 分配新 tensor)。与旧 update_trt_params 路径开销相当,但如果 XQAAttnOp 始终有 update_kv_cache_offset(从 .pyi 看确实有),此分支不会被触发,问题不大。建议确认是否为 dead code 并加注释说明。
  • xqa prepare_cuda_graph 中 rope_params.sequence_lengths 使用赋值而非 in-place copy @ rtp_llm/models_py/modules/factory/attention/cuda_impl/xqa.py:135
    • 建议:考虑改为 in-place copy 模式(类似 captured_seq_lens 的处理方式):在初始化时保存引用,replay 时用 copy 更新数据。当前因 XQA 内部处理 RoPE(need_rope_kv_cache 通常为 False)暂不触发,但若未来配置变化可能导致 CUDA graph replay 读到过期数据。标记为 unverified — 需确认 XQA decode 路径是否一定 need_rope_kv_cache=False。
  • ROCm fallback 路径逐个 fill_ 导致多次 kernel launch @ rtp_llm/cpp/cuda_graph/cuda_graph_runner.cc:369
    • 建议:考虑为 ROCm 实现类似的 fused fill kernel(或用 hipMemsetAsync 批量清零),减少 ROCm decode 热路径上的 kernel launch 次数。当前约 5-10 次独立 launch 可合并为 1 次。
  • LinearKVCacheGroup::getNeedBlocks 从 O(1) 变为 O(N) 线性扫描 @ rtp_llm/cpp/cache/LinearKVCacheGroup.cc:48
    • 建议:getNeedBlocks 在 scheduler 热路径中被频繁调用。应将 should_materialize 的 step_hit / tail / reserve 三种贡献用数学公式计算(类似旧版),避免长序列场景下 O(N) 遍历。新增的 tail-1 / tail-2 逻辑可以通过对旧公式做小修正实现。
  • HybridTypeKVCacheAllocator rollback 用 unordered_set 查找,可优化 @ rtp_llm/cpp/cache/HybridTypeKVCacheAllocator.cc:201
    • 建议:可考虑:(1) 使用 sorted vector + binary search 替代 unordered_set;(2) 或在 malloc 阶段记录新分配的 block index 列表,rollback 时直接释放该列表,无需 set diff。这样 rollback 是 O(新分配数) 而非 O(总块数)。
  • CpuTpBroadcaster::broadcast 串行写所有 peer @ rtp_llm/cpp/distribute/CpuTpBroadcaster.cc:411
    • 建议:对于 >2 个 peer 的场景,可用 writev 或 epoll 并发写多个 fd,或用 tree topology(rank 0→rank 1/2, rank 1→rank 3/4)将 O(N) 写降为 O(log N) 轮。当前实现对 tp_size<=4 影响可控,但 tp_size>=8 会有明显差异。
  • maxTokenNum 中 reserve_tokens 翻倍可能过度保守 @ rtp_llm/cpp/engine_base/stream/GenerateStream.cc:658
    • 建议:确认 2x 的余量是否必要。如果 async 路径的 conservative scheduling 已覆盖了最大 accept_len 的 KV block 预留,这里可能只需 propose_step + 1(一次 decode 的最大 accept)而非 2x+1。过多 reserve 会导致请求提前被 needFinish 终止。
  • applyP2PSideChannelToStream 中同步 .to(cuda) 创建 GPU tensor @ rtp_llm/cpp/engine_base/stream/StreamCacheResource.cc:148
    • 建议:使用 .to(cuda_i32, /non_blocking=/true) 来避免同步等待。对于 single-element tensor 创建,也可考虑预分配在 pinned memory 上后 non_blocking copy。
  • CpuTpBroadcaster broadcast 无超时,peer 崩溃会导致 rank 0 永久阻塞 @ rtp_llm/cpp/distribute/CpuTpBroadcaster.cc:402
    • 建议:注释中说明这是设计决策('request-time waits are expected'),但如果一个 peer 崩溃后 socket 不关闭(例如 kernel hang),writeAll/readAll 会无限阻塞。建议加一个可配置的超时(例如 60s),超时后 log + abort,避免生产环境中 rank 0 进程永远挂起。
  • asyncDebugEnabled() 每次调用都解析环境变量,无缓存 @ rtp_llm/cpp/engine_base/stream/GenerateStateMachine.cc:13
    • 建议:与同文件 useStreamAsyncReserveTokens() 使用 static const bool 缓存方式保持一致。handleRunning 在每个 decode step 调用,getenv + string 构造的开销虽小但不必要。改为 static bool cached = ...; return cached;
  • GenerateStateMachine::releaseResource 通过裸指针访问 GenerateStream,生命周期不明确 @ rtp_llm/cpp/engine_base/stream/GenerateStateMachine.cc:161
    • 建议:StreamCacheResource 通过裸指针 stream_ 指回 GenerateStream,理论上 GenerateStateMachine 的生命周期被 GenerateStream 管理,但新增的 deferred release 路径意味着 releaseResource() 可能在 GenerateStream 析构期间被调用。当前设计文档说 'Lifetime is bound by the GenerateStream',建议增加 assert 或在 GenerateStream 析构中确保 pending count 为零。
  • KVCacheMemoryConnector::startCopyAsync lambda 中 task_copy_plan.reset() 放在 waitDone 前,copy_plan 生命周期可能过短 @ rtp_llm/cpp/cache/connector/memory/KVCacheMemoryConnector.cc:470
    • 建议:从代码意图看,这是为了提前释放 copy_plan 引用的内存块,没有功能问题(sendCopyPlan 已返回)。但建议添加注释说明为什么在 waitDone 前 reset 是安全的,避免后续维护者误解。
  • BlockIds::swap 格式化 size_t 使用 %d 会导致错误日志中数值截断 @ rtp_llm/cpp/cache/KVCacheResource.cc:48
    • 建议:将 %d 改为 %zu 以匹配 size_t 类型,RTP_LLM_CHECK_WITH_INFO 同一处也需修复。
  • shouldMaterializeBlock 声明无定义 — 死代码未清理 @ rtp_llm/cpp/cache/LinearKVCacheGroup.h:36
    • 建议:要么实现该方法(提取 malloc/getNeedBlocks 中的 should_materialize lambda),要么移除声明避免未来链接错误。
  • TRTAttnOp::prepare 中 .item<int32_t>() 引入 GPU 同步阻塞 @ rtp_llm/models_py/bindings/cuda/TRTAttnOp.cc:49
    • 建议:将 context_total_kv_length 的值通过 cu_kv_seqlens 最后一个元素的 device pointer 传入 kernel,避免 D2H 同步;或在 prepare 阶段用异步 D2H + event 代替 .item()

P3

  • to_cuda_async lambda 在 buildBertEmbeddingInputs 和 buildPyEmbeddingInputs 中重复定义 @ rtp_llm/cpp/models/PyWrappedModel.cc:322
    • 建议:to_cuda_async 逻辑完全相同,建议提取为 PyWrappedModel 的 private 成员函数,减少重复。
  • readEnvFlagOnce 在 NormalExecutor.cc 和 MtpExecutor.cc 重复定义 @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:19
    • 建议:可将 readEnvFlagOnce 移到公共 header(如 utils/EnvUtil.h)避免两处匿名命名空间重复。同理 holdSamplerInputHostBuffers 也重复了。
  • AsyncRunner::launch 在 task_done_ 为 false 时阻塞调用线程 @ rtp_llm/cpp/normal_engine/AsyncRunner.cc:33
    • 建议:launch() 方法会在前一个 task 未完成时阻塞,这是正确设计但不明显。建议在头文件注释中明确说明 "launch() blocks until the previous task completes",避免调用方误以为是无等待 enqueue。
  • readEnvFlagOnce 和 holdSamplerInputHostBuffers 在两个文件中重复定义 @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:22
    • 建议:提取到共享头文件或 utils,减少重复代码。
  • ensureModelInputsOnCuda 和 checkModelInputsOnCuda 在 NormalExecutor 和 MtpExecutor 中完全重复 @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:392
    • 建议:提取到基类 Executor 或共享 utility 中,用参数区分 log tag。
  • readEnvFlagOnce 在 NormalExecutor 和 MtpExecutor 中重复定义 @ rtp_llm/cpp/normal_engine/NormalExecutor.cc:23
    • 建议:readEnvFlagOnce 工具函数重复出现多次。建议提取到共享头文件(如 utils/EnvUtils.h)中复用。
  • asyncDebugEnabled() 重复定义在多个 TU 中 @ rtp_llm/cpp/normal_engine/NormalModelInputGatherer.cc:17
    • 建议:与 readEnvFlagOnce 类似,asyncDebugEnabled() 也在多个匿名命名空间重复定义,建议合并。
  • fillParamsMhaDevice 在 batch_size==0 时未设置 batch_indice/positions @ rtp_llm/models_py/bindings/cuda/FlashInferMlaParams.cc:628
    • 建议:batch_size==0 的 early return 设置了 decode_page_indptr/page_indice/paged_kv_last_page_len 但没有设置 batch_indice 和 positions 别名。虽然 batch_size=0 时这些张量不会被读取,但为了一致性可以补上。非阻塞。
  • pyi 存根中 decode_cu_seqlens_host 重复声明 @ rtp_llm/ops/librtp_compute_ops/__init__.pyi:209
    • 建议:移除 line 209 的属性声明(保留 @Property 定义),或移除 @Property(保留属性声明)。当前两者并存会导致类型检查器混淆。看起来是移除 input_lengths_d/prefix_lengths_d 属性时误将 property 替换成了 decode_cu_seqlens_host 而非直接删除。
  • GenerateStateMachine::handleRunning 中 asyncDebugEnabled() 每次调用 getenv @ rtp_llm/cpp/engine_base/stream/GenerateStateMachine.cc:7
    • 建议:使用 static const bool 缓存结果,类似 GenerateStream.cc 中 useStreamAsyncReserveTokens() 的实现方式。
  • LinearKVCacheGroup::malloc 中 should_materialize lambda 重复定义 @ rtp_llm/cpp/cache/LinearKVCacheGroup.cc:95
    • 建议:将 should_materialize 逻辑统一到 shouldMaterializeBlock 成员方法中,getNeedBlocks 和 malloc 都调用它。当前 .h 已声明但未实现。
  • maxTokenNum 返回 size_t 但内部比较混用 int 和 size_t @ rtp_llm/cpp/engine_base/stream/GenerateStream.cc:663
    • 建议:max_seq_len_ 和 reserve_tokens 都是 int,三目运算结果为 int,但 std::min 的另一个参数是 size_t。建议显式 static_cast<size_t> 包裹三目结果,消除编译器 signed/unsigned 比较警告。当前代码逻辑正确(修复了旧代码的下溢),仅为代码清晰度建议。

Checklist ✅ (56 items passed)

Strengths

  • tpSyncModelInputs 中将 shape hints 广播从 NCCL+cudaSyncAndCheck 改为 execBroadcastCpu(UDS),消除了主路径上的 GPU 同步开销
  • GPU packed buffer 的 pack/unpack 使用 fusedCopy 批量 D2D 替代逐 tensor copy_,减少 kernel launch 次数
  • buildPyAttentionInputs 中将 cu_seqlens/padding_offset 计算下沉到 GPU kernel(invokeBuildAttentionInputMetadata),消除了 CPU 端 cumsum + item() 的阻塞 D2H
  • H2D 拷贝统一改为 non_blocking=true + buffer_holder_ 持有 host 引用,保证异步安全性
  • setupKVCacheForAttentionInputs 中 MHA 路径完全消除了 host block table 的分配和拷贝,改为 zero-copy device slice
  • TensorHolder::release() 采用双缓冲(tensors ↔ clear_tensors),确保 async 拷贝的 source tensor 多存活一个 release 周期
  • ExpertBalancer 在 world_size==1 时跳过 allReduce/broadcast,避免单机部署的无效通信开销
  • TensorHolder 的双缓冲 release 机制(clear_tensors = std::move(tensors))优雅地解决了 async H2D 源张量的生命周期问题,无需每个调用点自行管理
  • AsyncRunner 设计精良:单任务串行 + cv_done_ 栅栏 + event_.record 保证 GPU 依赖可见性,避免了多任务竞争
  • device-state 优化路径有完善的 fallback:每个 fast path 都有条件检查,不满足时自动退回 CPU host 路径,不会导致功能回退

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants