0.8MB 跑通 Qwen|第 15-2 篇:推理引擎的 prefill 与 decode——两条路径为何分开

简介: 本篇详解Qwen推理引擎中prefill与decode双路径设计:prefill一次性处理整段prompt(如18 token),批量写入KV缓存;decode逐token循环生成,追加KV。通过RK3588真机gdb断点实证,明确二者独立入口、状态流转与性能动因,手搓零依赖纯C引擎的核心逻辑。(239字)

系列:《0.8MB 跑通 Qwen:从零实现 ARM 零依赖纯 C 推理引擎》(30 天 × 90 篇) | 适配模型:Qwen3-VL-8B-Instruct(千问3_VL_8B_Instruct)· Qwen3-VL-2B-Instruct · Qwen3-30B-A3B | 测试设备:RK3588(4×Cortex-A76 + 4×Cortex-A55,aarch64)

系列总纲:《0.8MB 跑通 Qwen》30 天 90 篇 · 总纲(阿里云社区)

上一篇:15-1《入口架构:参数 → 分发(serve/自检)→ 退出》 | 下一篇:15-3《单步走一次生成》

真机实测通过:本文实验已在 RK3588 板端实测完成(2026-09;方法学与原始记录见仓库 docs 与《实验脚本》目录)

一句话导读:prefill 与 decode:推理引擎一次请求先 1 次 prefill、再循环 N 次 decode,两条独立的前向路径为什么必须分开——本篇用 gdb 断点抓到的调用链作证据,看 prompt 整段写 KV、生成逐词补 KV。

关键词:手搓 Qwen 推理引擎、千问大模型推理、prefill、decode、token 循环、GDB、Qwen3-VL、零依赖纯 C

导语:一次请求先来一遍 prefill、再循环 N 次 decode,两条独立的前向路径为什么不能合并?prompt 是现成的、可以批着算,生成的词却因因果依赖只能一个一个来,KV 的生命周期也彼此不同。本篇用 gdb 断点抓到的调用链作证据,看整段写 KV 与逐词补 KV 的分工。

15-1 里我们区分了"教材内核"与"生产路径"。今天把生产路径的两条前向摆开:prefill(一次吃下整段 prompt)与 decode(一次推进一个 token)——serve 每处理一个请求,先走前者、再循环后者。gdb 断点抓到的调用链就是证据。

1. 知识点:一次请求 = 1 次 prefill + N 次 decode

任何请求的"算"都分成两种形态:

prefill decode
输入 整段 prompt(如 18 个 token) 1 个新 token
前向数 1 次(内部按 mini-batch 分批走层) 每 token 1 次
产出 每个位置算出 KV 并缓存;末位 logits 下一步的 logits
时机 请求开头一次 然后循环到 EOS/max_tokens

为什么分开而不是"全按单 token 循环"?三个理由:

  1. prompt 是现成的,可以批:18 个 token 可以 18 路并行走一层、权重只读一遍(Day 14 的"读一遍"红利在 prefill 内部天然存在)——若当成 18 次 decode 串行做,权重要读 18 遍;
  2. decode 的词是现算的,只能串行:第 t+1 个 token 依赖第 t 个的输出(13-1 的因果),必须一次一个;
  3. KV 生命周期不同:prefill 把整段历史写进缓存(只写一次);decode 每步在缓存尾部追加一行,同时要读全历史做注意力(Day 8-10)。

引擎里两者各有独立入口(不是同一个函数加个 flag):prefill 走 st_qwen_model_prefill_batch(vllm_safetensors.c 第 10471 行起),decode 走 st_qwen_model_forward(第 9336 行起)——Day 10 稀疏的 prefill/decode 两套内核、Day 14 的批量前向(st_qwen_model_forward_batch)都是在这两条主干上的变体。

2. 对应代码:serve 层怎么编排这两步

请求处理函数 run_completion(vllm_server.c)先调 prefill(第 1321 行),再进 decode_loop(第 1334 行):

ist_reset(ctx, keep);                                    /* Day 11:保留前缀或清零 */
st_qwen_model_prefill_batch(st, ids + keep, n_ids - keep); /* ① prefill:只算增量 */
...
int rc = decode_loop(ctx, temperature, top_p, min_p, max_tokens,
                     stream, conn, out, met, ...);         /* ② decode:逐词循环 */

decode_loop 的循环体(第 1105–1110 行附近)每轮做:采样 argmax(st->logits) → gen++ → st_qwen_model_forward(st, id)——一次循环一个 token、一次前向(Day 13-1 的"1 循环 = 1 词 = 1 前向"就在这里)。

3. 改动后果:gdb 断点实录——18 个 token 的请求怎么被拆开

口径:RK3588 / 板端 Debug 构建 / 2026-09 / gdb 断在 st_qwen_model_prefill_batch 与 st_qwen_model_forward,客户端发一条 Say hello in one word.。

① prefill 命中(请求开头,缓存为空):

== PF-HIT n_cache0=0 seq_len=0 ==
#0  st_qwen_model_prefill_batch (st=..., token_ids=..., n_tokens=18)
    at vllm_safetensors.c:10471
#1  run_completion (...) at vllm_server.c:1321
#2  handle_chat (...)    at vllm_server.c:2559
#3  server_handler (...) at vllm_server.c:3190
[PREFILL-TIMING] n=18 total=6296.1ms | GEMM=6207.9ms(98.6%) ATTN=23.9ms( 0.4%)

注意传给 prefill 的 prompt 是拼好聊天模板后的完整文本:<|im_start|>user\nSay hello in one word.<|im_end|>\n<|im_start|>assistant\n——18 个 token,一次 prefill 全吃下(Day 21 讲模板注入)。

② decode 命中(prefill 之后,缓存已有 18 行):

== DEC-HIT token_id=9707 seq_len(before)=18 cache0=18 ==
#0  st_qwen_model_forward (st=..., token_id=9707) at vllm_safetensors.c:9336
#1  decode_loop (...)    at vllm_server.c:1109
#2  run_completion (...) at vllm_server.c:1334

判读:

  1. 调用链是分叉的:prefill 的上一帧是 run_completion:1321,decode 的上一帧是 run_completion:1334 → decode_loop:1109——同一请求、两条独立路径,各自的调用栈形状不同;
  2. 状态变化对上了:prefill 进入时 cache0=0, seq_len=0(18 个 token 还没写缓存);decode 进入时 seq_len=18, cache0=18——18 行 KV 正是 prefill 写下的,decode 从第 18 位开始追加;
  3. 一次请求 = 1×prefill + ≤8×decode:max_tokens=8,所以 decode 最多 8 次前向(模型输出 Hello 后提前 EOS 停止,实际更少);
  4. 诚实标注:这是 -O0 Debug 构建,18-token prefill 花了 6.3s(Release 下同样 18 token 是 ~0.15s 量级,Day 3-10 的口径);gdb 实证看的是结构与状态,不是速度。

4. 学员调试任务

  • A 档(板端动手):用 Debug 构建复刻第 3 节(gdb 批模式断 prefill/forward,发一条 2 词以内的请求),抄出 PF 与 DEC 两段 backtrace 与 n_tokens/seq_len,回答:这请求被拆成 1 次 prefill + 几次 decode?为什么不是每次请求都 decode 满 max_tokens?
  • B 档(纯读源码):读 run_completion 的 prefill 调用(vllm_server.c 1321)与 decode_loop 的循环体(1105–1110),回答:① ids + keep 的 keep 从哪来(提示:Day 11)?② decode 循环每轮的"采样→forward"顺序为什么不能反过来?③ prefill 与 decode 各自用哪套注意力内核(Day 10 的两个函数名)?

预期输出:你能用两段 gdb backtrace 讲清"prefill 一次写 18 行 KV、decode 一次补 1 行"的数据流,并指出两条路径的代码入口行号。

收尾

  • 本篇源码点名:vllm_safetensors.c(st_qwen_model_prefill_batch 10471、st_qwen_model_forward 9336、批量前向 9829)、vllm_server.c(run_completion 1321/1334、decode_loop 1105–1110)。
  • 开源仓库:Kestrel-LLM (Gitee)(AGPL-3.0-or-later 或商业许可,二选一)
  • 下篇预告:路径拆开了,最后一关是"真的走一遍"——15-3 用 gdb 从 prefill 断点一路单步进 decode,逐词看 token 循环与 KV 追加,直到 EOS 停表。
相关文章
|
1天前
|
缓存 调度
聊天记录越长越贵:我的上下文压缩分了七层
Agent 对话越长 token 越贵、模型越迷糊,但压缩本身也有成本。本文附我开源项目 codeAgent 的真实源码:compress_if_needed 分层压缩总调度——从免费的时间清理、L1 裁中间、L2 单条折叠,到最贵的 L4 LLM 摘要,共七层流水线各管一档;L4 还带四套保命机制(9 段式结构化 prompt、PTL 重试、熔断器、预提取记忆替代),摘要请求本身还设计成能命中前缀缓存。
29 0
|
1天前
|
缓存 人工智能 索引
0.8MB 跑通 Qwen|第 12-1 篇:LLM 推理的跨进程恢复——服务重启了,会话不能断
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,实现在RK3588上部署Qwen3-VL等多尺寸模型。本文详解`--disk-kv`机制:将KV Cache落盘持久化,支持进程重启后按前缀精准恢复,实现“会话不断连”,2K上下文prefill加速达23.6×,真正打通边缘AI服务可用性最后一环。(239字)
|
1天前
|
缓存
0.8MB 跑通 Qwen|第 8-3 篇:推理引擎的 KV 缓存结构——按 token 还是按头存
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,适配Qwen3-VL多模态模型,在RK3588上实测。本文精析KV缓存的token-major布局设计原理,揭示其如何兼顾prefill与decode访存效率,直击带宽瓶颈。(239字)
|
1天前
|
C++
0.8MB 跑通 Qwen|第 10-2 篇:推理引擎的 sparse top-k 块选择——"只看该看的"到底怎么选
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,实测RK3588上高效运行Qwen3-VL多模态模型。本文深度剖析sparse_attn_head的top-k块选择机制——探针抽样、prefill重要性预留、贪心补满与recency保险四段代码,揭示长上下文稀疏注意力如何精准“找针”,并实证k=1时板端翻车根源。(239字)
|
1天前
|
调度
0.8MB 跑通 Qwen|第 13-3 篇:推理引擎的回退与收益边界——哪些场景 spec 才划算
本文精析Speculative Decoding在ARM端的收益边界:基于RK3588实测,揭示“62%命中率仍变慢”的根源——单步Decode仅44ms时,验证开销与KV回滚反致负增益;明确开启条件:长上下文、高重复性、大模型、贪婪采样。零依赖纯C,适配Qwen3-VL系列。
|
1天前
|
人工智能 图形学
婚庆建模500元变2元,她靠AI年入200万:AI婚庆培训OPC案例深度拆解
本文为「OPC一人公司通关手册」第24篇,深度拆解96年婚庆从业者如何用AI重构行业:将高端方案从3-7天压缩至1.5小时,建模成本从千元降至2元,进而转型AI培训,一年营收200万+。核心启示:一人公司成败不在工具,而在“专业底盘+AI放大”,卖认知差远比卖时间更可持续。(239字)
|
1天前
|
人工智能 自然语言处理 数据可视化
万小智AI建站3.0全新升级:一句话,企业官网发布上线全流程
阿里云万小智3.0是AI驱动的智能建站工具,用户只需用自然语言描述需求,AI即可自动生成完整网站。本文详解从创建应用、定义需求、对话细化、确认PRD到预览编辑、发布上线的全流程,助您快速搭建专业网站。(239字)
|
1天前
|
人工智能 Linux Windows
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面
千问办公(QwenWork)是阿里云推出的AI智能办公平台,支持网页端直接使用及Windows/Mac/Linux客户端下载。提供PPT生成、财报分析、网页搭建等AI功能,个人版免费,企业版198元/席/月。详情见官网qwenwork.cn或阿里云产品页。
186 0
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面
|
1天前
|
弹性计算 编解码 人工智能
阿里云服务器ECS实例架构:X86计算和Arm计算有什么区别?GPU、裸金属和高性能计算区别对比?
阿里云ECS支持五大计算架构:X86(稳定通用,适配Intel/AMD)、Arm(倚天/Altra,高能效独享核心)、GPU(AI训练/图形加速)、弹性裸金属(神龙架构,物理机性能+虚拟机弹性)、高性能计算(HPC优化,超大规格)。按场景灵活选型。阿里云服务器ECS官网:https://t.aliyun.com/U/AZBUsA
|
1天前
|
应用服务中间件
阿里云轻量应用服务器最新费用:2核2G、2核4G、4核8G、4核16G都有活动,秒杀38元1年起
阿里云轻量应用服务器2026年最新报价:新用户专享,2核2G仅38元/年(秒杀)、2核4G 379元、4核8G 1159元、4核16G 1599元;全系标配200M峰值带宽+不限流量,性价比突出。(239字)
36 0

热门文章

最新文章