Skip to content

[BugFix][PD Disaggregation][KVCache] Fix low cache hit rate in PD split (disaggregation) scenario#7364

Merged
kevincheng2 merged 1 commit intoPaddlePaddle:developfrom
kevincheng2:fix/pd-split-cache-hit-rate
Apr 14, 2026
Merged

[BugFix][PD Disaggregation][KVCache] Fix low cache hit rate in PD split (disaggregation) scenario#7364
kevincheng2 merged 1 commit intoPaddlePaddle:developfrom
kevincheng2:fix/pd-split-cache-hit-rate

Conversation

@kevincheng2
Copy link
Copy Markdown
Collaborator

@kevincheng2 kevincheng2 commented Apr 13, 2026

Motivation

在 PD 分离(disaggregation)场景下,prefill 节点收到请求后,未能及时更新 prefix cache block 的命中信息,导致 prefix cache 命中率异常偏低,影响推理性能。

具体问题:

  • prefill 节点在通过 _allocate_gpu_blocks 成功分配block后,没有调用 update_cache_blocks 更新 cache block 状态,导致已命中的 prefix cache 无法被正确记录

Modifications

  1. 统一 prefill 节点的 cache block 更新:在 _free_blocks_when_stop 方法中,新增对 splitwise_role != "prefill" 的排除判断,避免 prefill 节点重复更新 cache block 状态。

  2. prefill 节点主动更新 cache block:在 preallocate_resource_in_p 方法中,prefill 节点成功分配block后,主动调用 update_cache_blocks(使用 need_prefill_tokens)。

Usage or Command

启动 PD 分离服务进行验证:

# 启动 router
bash run_router.sh

# 启动 P 节点(prefill)
bash run_p.sh

# 启动 D 节点(decode)
bash run_d.sh

# 发送请求并观察 cache hit rate 日志
curl -X POST http://${SERVER_IP}:${SERVER_PORT}/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model": "your-model", "messages": [{"role": "user", "content": "..."}]}'

修复后,prefill 节点日志中 prefix cache 命中率应显著提升。

Checklist

  • Add at least a tag in the PR title.
  • Format your code, run pre-commit before commit.
  • Add unit tests. Please write the reason in this PR if no unit tests. (该 bug fix 涉及运行时状态更新,需要端到端 PD 分离环境验证,单元测试覆盖困难)
  • Provide accuracy results.
  • If the current PR is submitting to the release branch, make sure the PR has been submitted to the develop branch, then cherry-pick it to the release branch with the [Cherry-Pick] PR tag.

…it scenario

## Motivation

在 PD 分离场景下,decode 节点在接收 prefill 节点转发的请求后,没有及时更新 cache block 的命中信息,
导致 prefix cache 命中率低,影响推理性能。

## Modifications

1. 在 `_free_blocks_when_stop` 方法中,额外排除 prefill 节点(`splitwise_role == "prefill"`)
   的 cache block 更新,避免 prefill 节点重复更新 cache 导致状态混乱。
2. 在 decode 节点分配请求(`_alloc_requests_with_cache`)成功后,主动调用
   `update_cache_blocks` 使用 `need_prefill_tokens` 更新 cache block 信息,
   确保 decode 节点能正确感知已命中的 prefix cache。
@paddle-bot
Copy link
Copy Markdown

paddle-bot bot commented Apr 13, 2026

Thanks for your contribution!

Copy link
Copy Markdown

@PaddlePaddle-bot PaddlePaddle-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 AI Code Review | 2026-04-13

📋 Review 摘要

PR 概述:修复 PD 分离场景下 D 节点 prefix cache 命中率偏低的问题,通过调整 P 节点的 cache block 更新时机

变更范围fastdeploy/engine/sched/resource_manager_v1.py

影响面 Tag[PD Disaggregation] [KVCache]

📝 PR 规范检查

  • ✅ 标题包含有效的 [BugFix][PD Disaggregation][KVCache] Tag
  • ✅ PR 描述包含 Motivation 和 Modifications 章节
  • ✅ 包含 Usage 或 Command 验证步骤
  • ✅ Checklist 标注无单元测试原因

说明:PR 描述中的方法名与实际修改不符(描述中提到 _free_blocks_when_stop_alloc_requests_with_cache,实际修改的是 _allocate_decode_and_extend()preallocate_resource_in_p()),建议下次检查确保描述与实际代码一致。

问题

级别 文件 概述
🟡 建议 resource_manager_v1.py:1380 update_cache_blocks 参数选择可能导致 cache state 不一致

总体评价

修复思路合理,通过在 preallocate_resource_in_p() 中新增 update_cache_blocks 调用,确保 P 节点正确更新 cache tree,同时避免 prefill 循环中的重复更新。但修改 2 中使用 request.need_prefill_tokens 作为参数的合理性需要进一步说明。

self.req_dict[request.request_id] = allocated_position

self.cache_manager.update_cache_blocks(
request, self.config.cache_config.block_size, request.need_prefill_tokens
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 建议preallocate_resource_in_p() 中,使用 request.need_prefill_tokens 作为 update_cache_blocks 的第三个参数需要进一步说明。

update_cache_blocks 的第三个参数语义是 num_computed_tokens(已计算的 tokens 数量),而 get_prefix_cached_blocks() 已经设置了 request.num_computed_tokens 为命中的 token 数量(见第 1278 行)。

当只有部分 prompt tokens 命中 cache 时(matched_token_num < need_prefill_tokens):

  • request.need_prefill_tokens = 所有 prompt tokens
  • request.num_computed_tokens = 命中的 tokens

使用 need_prefill_tokens 可能导致 cache tree 中记录的 cached blocks 数量与实际不一致。建议验证在部分 cache 命中场景下,使用 request.num_computed_tokens 是否更合适:

self.cache_manager.update_cache_blocks(
    request, self.config.cache_config.block_size, request.num_computed_tokens
)

如果使用 need_prefill_tokens 是有意为之(例如让 P 节点统一记录完整 prompt),建议在 PR 描述中补充说明。

@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (develop@738c658). Learn more about missing BASE report.

Additional details and impacted files
@@            Coverage Diff             @@
##             develop    #7364   +/-   ##
==========================================
  Coverage           ?   73.05%           
==========================================
  Files              ?      383           
  Lines              ?    53613           
  Branches           ?     8411           
==========================================
  Hits               ?    39167           
  Misses             ?    11782           
  Partials           ?     2664           
Flag Coverage Δ
GPU 73.05% <100.00%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

该 PR 旨在修复 PD 分离(disaggregation)场景下 prefix cache 命中率偏低的问题,通过调整 prefix cache block 状态更新时机/角色来提升推理性能。

Changes:

  • 在调度 prefill 逻辑中,进一步限制 update_cache_blocks 的触发角色(排除 prefill)。
  • preallocate_resource_in_p 中新增一次 update_cache_blocks 调用,以期在资源预分配阶段更新 cache block 状态。
Comments suppressed due to low confidence (1)

fastdeploy/engine/sched/resource_manager_v1.py:934

  • 这里把 update_cache_blocks 的触发条件改成仅在 splitwise_role 既不是 "decode" 也不是 "prefill" 时才执行(实际只剩 mixed)。但在 v1 splitwise 流程里,P 节点会通过 add_request_in_p 直接把请求放进 running 队列(不会走 waiting->running 那段 update_cache_blocks 逻辑);因此该改动会导致 P 节点在 prefill 过程中 num_computed_tokens 增长时不再更新 radix tree/leaf 记录,新计算出来的 prefix blocks 可能永远不会被纳入 prefix cache,命中率/复用反而会继续偏低。建议明确 splitwise 场景下由哪个角色负责在“prefill 真正完成计算后”调用 update_cache_blocks:要么恢复 P 节点在此处更新(并在其它位置避免重复/冲突),要么把更新迁移到 decode 或 cache_messager 的确定时机,并确保不会在计算完成前把未填充 block 写入树。
                    if (
                        self.config.cache_config.enable_prefix_caching
                        and self.config.scheduler_config.splitwise_role != "decode"
                        and self.config.scheduler_config.splitwise_role != "prefill"
                    ):
                        self.cache_manager.update_cache_blocks(
                            request, self.config.cache_config.block_size, request.num_computed_tokens
                        )

Comment on lines +1379 to +1380
self.cache_manager.update_cache_blocks(
request, self.config.cache_config.block_size, request.need_prefill_tokens
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

preallocate_resource_in_p 里调用 update_cache_blocks 时传入 request.need_prefill_tokens 会让 PrefixCacheManager 认为整段 prompt 都已“计算完成”,从而在 mm_build_path 中为 cache miss 部分也提前创建 radix tree 节点并绑定新分配的 GPU block。由于这些 block 在真正的 prefill forward 前还没有写入有效 KV,这会让后续请求有机会匹配到未填充/未完成的 cache,造成错误复用或状态污染。建议这里改为使用 request.num_computed_tokens(即命中后可直接复用的已计算 token 数)来做 hit 记录,或者把 update_cache_blocks 延后到 prefill 计算完成并确认对应 block 已填充之后再调用。

Suggested change
self.cache_manager.update_cache_blocks(
request, self.config.cache_config.block_size, request.need_prefill_tokens
# Only record the prefix tokens that are already computed and safe to reuse.
computed_prefix_tokens = max(
0, min(request.num_computed_tokens, request.need_prefill_tokens)
)
self.cache_manager.update_cache_blocks(
request, self.config.cache_config.block_size, computed_prefix_tokens

Copilot uses AI. Check for mistakes.
@kevincheng2 kevincheng2 merged commit ff47701 into PaddlePaddle:develop Apr 14, 2026
39 of 42 checks passed
EmmonsCurse pushed a commit to EmmonsCurse/FastDeploy that referenced this pull request Apr 14, 2026
…it scenario (PaddlePaddle#7364)

## Motivation

在 PD 分离场景下,decode 节点在接收 prefill 节点转发的请求后,没有及时更新 cache block 的命中信息,
导致 prefix cache 命中率低,影响推理性能。

## Modifications

1. 在 `_free_blocks_when_stop` 方法中,额外排除 prefill 节点(`splitwise_role == "prefill"`)
   的 cache block 更新,避免 prefill 节点重复更新 cache 导致状态混乱。
2. 在 decode 节点分配请求(`_alloc_requests_with_cache`)成功后,主动调用
   `update_cache_blocks` 使用 `need_prefill_tokens` 更新 cache block 信息,
   确保 decode 节点能正确感知已命中的 prefix cache。
@EmmonsCurse
Copy link
Copy Markdown
Collaborator

✅ Cherry-pick successful! Created PR: #7387

kevincheng2 added a commit that referenced this pull request Apr 14, 2026
…it scenario (#7364) (#7387)

## Motivation

在 PD 分离场景下,decode 节点在接收 prefill 节点转发的请求后,没有及时更新 cache block 的命中信息,
导致 prefix cache 命中率低,影响推理性能。

## Modifications

1. 在 `_free_blocks_when_stop` 方法中,额外排除 prefill 节点(`splitwise_role == "prefill"`)
   的 cache block 更新,避免 prefill 节点重复更新 cache 导致状态混乱。
2. 在 decode 节点分配请求(`_alloc_requests_with_cache`)成功后,主动调用
   `update_cache_blocks` 使用 `need_prefill_tokens` 更新 cache block 信息,
   确保 decode 节点能正确感知已命中的 prefix cache。

Co-authored-by: kevin <chengyf112@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants