Skip to content

[Optimization] Reduce logprob processing overhead by using actual topk instead of fixed K+1#7860

Open
Sunny-bot1 wants to merge 1 commit into
PaddlePaddle:developfrom
Sunny-bot1:opt_logprob_process
Open

[Optimization] Reduce logprob processing overhead by using actual topk instead of fixed K+1#7860
Sunny-bot1 wants to merge 1 commit into
PaddlePaddle:developfrom
Sunny-bot1:opt_logprob_process

Conversation

@Sunny-bot1
Copy link
Copy Markdown
Collaborator

@Sunny-bot1 Sunny-bot1 commented May 19, 2026

Motivation

开启 logprob(top_logprobs=0)时,性能下降明显,TPOT 比不开 logprob 高约 5ms。分析发现开销大的原因之一是 logprob 数据传输和处理均按固定的 K+1=21 列处理,而用户实际只需要 actual_topk=1 列,存在约 10 倍的冗余计算。

save_output_topk C++ op 中:

  • sender 循环固定写入 K+1=21 列,stride 固定为 K+1
  • mtext[1] 只存 bsz,actual_topk 信息丢失

get_output_topk C++ op 中:

  • receiver 循环固定读取 K+1=21 列,stride 固定为 K+1

token_processor.py 中:

  • reshape 固定按 K+1=21 列展开,导致 output_scores.numpy()
    拷贝 batch*21 个 float(10752),以及 per-request tolist()
    每行处理 21 个元素

Modifications

save_output_msg_with_topk.cc

  • mtext[1] 改为 bit-pack 存储:bsz(低16位)| actual_topk(高16位)
  • sender 循环改为 max_num_logprobs 次,stride 改为 max_num_logprobs

get_output_msg_with_topk.cc

  • 从 mtext[1] 解包 bsz 和 actual_topk
  • receiver 循环改为 actual_topk 次,stride 改为 actual_topk

token_processor.py

  • 从 packed mtext[1] 解包 batch 和 actual_topk
  • output_scores.numpy() 切片范围从 batch21 缩小到 batchactual_topk
  • reshape 列数从固定 K+1 改为动态 actual_topk

msgsnd/msgrcv 消息结构体大小不变,向后兼容。
actual_topk 通过 mtext[1] bit-pack 传递,不增加额外字段。

top_logprobs=0,concurrency=256,GLM-4.5-Air,TP8:

指标 优化前 优化后 提升
平均TPOT 33.80ms 31.41ms -2.39ms
平均解码速度 30.72 32.79 +6%
QPS 0.571 req/s 0.590 req/s +3%

Usage or Command

Accuracy Tests

Checklist

  • Add at least a tag in the PR title.
    • Tag list: [[FDConfig],[APIServer],[Engine], [Scheduler], [PD Disaggregation], [Executor], [Graph Optimization], [Speculative Decoding], [RL], [Models], [Quantization], [Loader], [OP], [KVCache], [DataProcessor], [BugFix], [Docs], [CI], [Optimization], [Feature], [Benchmark], [Others], [XPU], [HPU], [GCU], [DCU], [Iluvatar], [Metax]]
    • You can add new tags based on the PR content, but the semantics must be clear.
  • Format your code, run pre-commit before commit.
  • Add unit tests. Please write the reason in this PR if no unit tests.
  • 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.

@paddle-bot
Copy link
Copy Markdown

paddle-bot Bot commented May 19, 2026

Thanks for your contribution!

@Sunny-bot1 Sunny-bot1 changed the title [Optimization][Logprob] Reduce logprob processing overhead by using actual topk instead of fixed K+1 [Optimization] Reduce logprob processing overhead by using actual topk instead of fixed K+1 May 19, 2026
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.

🤖 Paddle-CI-Agent | pr_review | 2026-05-19 21:05:13

📋 Review 摘要

PR 概述:通过 bit-pack 将 actual_topk 编入 mtext[1],使 logprob 数据传输与处理按实际列数而非固定 K+1=21 列进行,显著降低 TPOT。
变更范围custom_ops/gpu_ops/(C++ 消息收发 op)、fastdeploy/output/token_processor.py
影响面 Tag[OP] [DataProcessor]

问题

级别 文件 概述
❓ 疑问 custom_ops/gpu_ops/get_output_msg_with_topk.cc:100 参数 k 在函数体中不再使用,可能触发 -Wunused-parameter 编译警告
🟡 建议 custom_ops/gpu_ops/save_output_msg_with_topk.cc:121 custom op 变更未在 tests/operators/ 补充单测(A3 必查项)

📝 PR 规范检查

标题含非官方 Tag [Logprob](不在 §D2 Tag list 中),按规范标题应仅含一个官方 Tag;Usage or CommandAccuracy Tests 两节内容为空(仅占位符);Checklist 全部未勾选。

标题建议(可直接复制):

  • [Optimization] Reduce logprob processing overhead by using actual topk instead of fixed K+1

PR 描述建议(可直接复制,必须复刻 checklist §D2 模板的完整结构):

## Motivation

开启 logprob(top_logprobs=0)时,性能下降明显,TPOT 比不开 logprob 高约 5ms。分析发现原因是 logprob 数据传输和处理均按固定的 K+1=21 列处理,而用户实际只需要 actual_topk=1 列,存在约 10 倍的冗余计算。

## Modifications

**save_output_msg_with_topk.cc**
- mtext[1] 改为 bit-pack 存储:bsz(低16位)| actual_topk(高16位)
- sender 循环改为 max_num_logprobs 次,stride 改为 max_num_logprobs

**get_output_msg_with_topk.cc**
- 从 mtext[1] 解包 bsz 和 actual_topk
- receiver 循环改为 actual_topk 次,stride 改为 actual_topk

**token_processor.py**
- 从 packed mtext[1] 解包 batch 和 actual_topk
- output_scores.numpy() 切片范围从 batch*21 缩小到 batch*actual_topk
- reshape 列数从固定 K+1 改为动态 actual_topk
- 提前将全量数组转换为 Python list,避免循环内多次 .tolist()

## Usage or Command

N/A(性能优化,无新增接口或命令)

## Accuracy Tests

N/A(本次变更仅优化数据传输和处理流程,不涉及模型前向计算,不影响输出精度)

## Checklist

- [x] Add at least a tag in the PR title.
  - Tag list: [`[FDConfig]`,`[APIServer]`,`[Engine]`, `[Scheduler]`, `[PD Disaggregation]`, `[Executor]`, `[Graph Optimization]`, `[Speculative Decoding]`, `[RL]`, `[Models]`, `[Quantization]`, `[Loader]`, `[OP]`, `[KVCache]`, `[DataProcessor]`, `[BugFix]`, `[Docs]`, `[CI]`, `[Optimization]`, `[Feature]`, `[Benchmark]`, `[Others]`, `[XPU]`, `[HPU]`, `[GCU]`, `[DCU]`, `[Iluvatar]`, `[Metax]`]
  - You can add new tags based on the PR content, but the semantics must be clear.
- [ ] Format your code, run `pre-commit` before commit.
- [ ] Add unit tests. Please write the reason in this PR if no unit tests.
- [ ] 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.

总体评价

整体思路清晰,通过 bit-pack 编码 actual_topk 消除了 K+1 固定宽度的冗余传输,C++ 与 Python 两端逻辑对称,消息结构体大小不变保证向后兼容。建议补充 operators 单测并修正 PR 规范后合入。

for (int i = 0; i < bsz; i++) {
for (int j = 0; j < k + 1; j++) {
const int64_t offset = i * (K + 1) + j;
for (int j = 0; j < actual_topk; j++) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❓ 疑问 参数 k 在函数体中已不再使用(原 for (int j = 0; j < k + 1; j++) 已替换为 actual_topk),但仍保留在函数签名和 PD_BUILD_STATIC_OPAttrs 中(为保持调用侧 API 兼容性),可能触发 -Wunused-parameter 编译器警告。

建议在函数体开头显式抑制并注释原因:

(void)k;  // retained for op-attr API compatibility; actual loop bound is from packed mtext[1]

// densely in the message buffer.
for (int j = 0; j < max_num_logprobs; j++) {
const int64_t offset = i * max_num_logprobs + j;
if (j == 0) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 建议 根据 checklist A3,custom_ops/ 下 op 变更需要在 tests/operators/ 补充单测。本次修改了 bit-pack 编码和 dense stride 传输逻辑,建议至少覆盖以下场景:

  1. bsz=1, actual_topk=1(top_logprobs=0 场景)
  2. bsz=N, actual_topk=K+1(满 topk 场景,验证不越界)
  3. 验证 save/get 端解包后数据一致性

若 CI 环境暂不支持运行 op 单测,请在 PR 描述中说明原因。

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented May 19, 2026

Codecov Report

❌ Patch coverage is 66.66667% with 4 lines in your changes missing coverage. Please review.
⚠️ Please upload report for BASE (develop@dad5a43). Learn more about missing BASE report.

Files with missing lines Patch % Lines
fastdeploy/output/token_processor.py 66.66% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             develop    #7860   +/-   ##
==========================================
  Coverage           ?   63.36%           
==========================================
  Files              ?      462           
  Lines              ?    64370           
  Branches           ?     9871           
==========================================
  Hits               ?    40786           
  Misses             ?    20810           
  Partials           ?     2774           
Flag Coverage Δ
GPU 72.47% <66.66%> (?)
XPU 7.12% <0.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.

@PaddlePaddle-bot
Copy link
Copy Markdown

PaddlePaddle-bot commented May 19, 2026

🤖 Paddle-CI-Agent | ci_status_monitor | 2026-05-20 01:38:14

CI报告基于以下代码生成(30分钟更新一次):


1 任务总览

2 个 required 任务失败,阻塞合并,需优先处理。

总执行(rerun次数) 总任务 ✅ 通过 ❌ 失败 ⏳ 运行中 ⏸️ 等待中 跳过
41(0) 41 36 4 0 1 0

2 任务状态汇总

2.1 Required任务 : 8/10 通过

必选任务阻塞合并,失败需优先处理。

状态 任务 耗时 根因 修复建议 日志 重跑
Run FastDeploy Unit Tests and Coverage / run_tests_with_coverage 1h20m PR问题:新packing格式导致actual_topk=0,reshape为空引发IndexError 更新测试,将output_tokens[1,0]改为新packing格式1|(K+1)<<16 Job -
xpu_4cards_case_test / run_xpu_4cards_cases 32m25s PR问题:XPU logprob测试超时300s,XPU后端未适配新packing格式 检查XPU后端是否已适配新packing格式(actual_topk<<16)|bsz Job -
其余 8 个必选任务通过 - - - - -

2.2 可选任务 — 28/31 通过

可选任务不阻塞合并,失败仅供参考。

状态 任务 耗时 日志 重跑
Check PR Template 11s Job -
Trigger Jenkins for PR 29m47s Job -
⏸️ CI_HPU - - -
其余 28 个可选任务通过 - - -

3 失败详情(仅 required)

Run FastDeploy Unit Tests and Coverage / run_tests_with_coverage — 用例失败(置信度: 高)

Run FastDeploy Unit Tests and Coverage / run_tests_with_coverage

  • 状态: ❌ 失败
  • 错误类型: 用例失败
  • 置信度: 高
  • 根因摘要: PR新packing格式导致actual_topk=0,tokens reshape为空引发IndexError
  • 分析器: ci_analyze_unittest_fastdeploy

失败用例:

测试 错误 根因
output/test_token_processor.py::test_process_batch_output_logprob_records_topk_and_caching IndexError: index 0 is out of bounds for axis 1 with size 0 测试未更新为新packing格式

根因详情:
PR将 output_tokens[1, 0] 从单纯的 bsz 改为打包值:低16位存 bsz,高16位存 actual_topk。测试中仍使用旧格式 processor.output_tokens[1, 0] = 1,导致 actual_topk = (1 >> 16) & 0xFFFF = 0,进而 tokens.reshape([batch, 0]) 产生空数组,在 token_id = int(tokens[i, 0]) 处抛出 IndexError。

关键日志:

packed = int(self.output_tokens[1, 0])  # = 1
batch = packed & 0xFFFF                  # = 1
actual_topk = (packed >> 16) & 0xFFFF   # = 0  ← 根因
tokens = tokens[2 : batch * actual_topk + 2].reshape([batch, actual_topk])
# → reshape([1, 0]) 空张量
token_id = int(tokens[i, 0])
E  IndexError: index 0 is out of bounds for axis 1 with size 0
# fastdeploy/output/token_processor.py:868

修复建议:

  1. tests/output/test_token_processor.py 中,将 processor.output_tokens[1, 0] = 1 改为 processor.output_tokens[1, 0] = 1 | ((K + 1) << 16),使高16位编码正确的 actual_topk
  2. 检查其他相关测试用例是否同样使用了旧格式的 output_tokens[1, 0] 赋值。

修复建议摘要: 更新测试,将output_tokens[1,0]改为新packing格式1|(K+1)<<16

关联变更: fastdeploy/output/token_processor.py_process_batch_output 新增 packed 解包逻辑

xpu_4cards_case_test / run_xpu_4cards_cases — 超时(置信度: 中)

xpu_4cards_case_test / run_xpu_4cards_cases

  • 状态: ❌ 失败
  • 错误类型: 超时
  • 置信度: 中
  • 根因摘要: XPU logprob测试请求超时300s,疑似XPU后端未适配新packing格式
  • 分析器: ci_analyze_unittest_fastdeploy

失败用例:

测试 错误 根因
test_logprobs-topk_21b_tp4.py::test_logprobs_mode ReadTimeoutError: Read timed out (300s) 服务端处理logprob请求超时

根因详情:
13/14 测试通过,仅 test_logprobs_mode 超时(整体耗时29m30s)。该测试专用于验证 logprobs 模式,与本 PR 直接相关。XPU 后端 C++ worker 可能未同步更新,仍向 output_tokens[1, 0] 写入旧格式(仅 bsz),Python 侧解出 actual_topk=0,导致后续处理逻辑出现异常,服务端无响应直至超时。

关键日志:

E  ReadTimeoutError: HTTPConnectionPool(host='127.0.0.1', port=8588):
   Read timed out. (read timeout=300)
FAILED tests/xpu_ci/4cards_cases/test_logprobs-topk_21b_tp4.py::test_logprobs_mode
1 failed, 13 passed in 1770.87s (0:29:30)

修复建议:

  1. 检查 XPU 后端(C++ worker 或 XPU kernel)中写入 output_tokens[1, 0] 的代码,确认是否已更新为新 packing 格式 (actual_topk << 16) | bsz
  2. 若 XPU 后端暂未修改,可在 Python 侧对 actual_topk == 0 时降级为使用固定的 K+1 作为 fallback,避免 reshape 出空张量。

修复建议摘要: 检查XPU后端是否已适配新packing格式(actual_topk<<16)|bsz

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.

3 participants