0.8MB 跑通 Qwen|第 9-1 篇:推理引擎的 KV 也量化——q8 KV 把缓存与 decode 带宽压到一半

简介: 本系列手搓零依赖纯C推理引擎,仅0.8MB即可在RK3588上跑通Qwen3-VL多模型。本文详解q8 KV量化:将f16 KV压至INT8,按token每头独立定标,缓存与decode带宽降低约48%,兼顾精度与效率,f32仅作调试探针。

系列:《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 篇 · 总纲(阿里云社区)

上一篇:8-3《KV 缓存结构:按 token 还是按头存》 | 下一篇:9-2《flash_attn_single_q_q8_neon:单 q 单遍扫全 KV》

源码精读篇:本文为源码/方法论精读,无独立实测;文中数字均引述仓库 docs 的板端实测记录

一句话导读:推理引擎的 q8 KV:把 K、V 从 f16 压到 INT8,按每 token 每头定标量化,逐字节算清缓存与 decode 扫描带宽省约一半的账,并讲清 KV 为何敢量化、f32 为何只是调试探针。

关键词:手搓 Qwen 推理引擎、千问大模型推理、Qwen3-VL、零依赖纯 C、q8 KV、KV cache、INT8 量化、带宽、decode

导语:KV 是模型里除权重外最肥的东西:8K 上下文用 f16 存 K、V 就约 560MB,decode 每词还要全扫一遍。手搓 Qwen 推理引擎默认把内存 KV 压成 INT8(q8),按每 token 每头单独定标,缓存与 decode 带宽都压到约一半。这篇算清这笔账,也讲清 KV 为何敢量化。

8-3 我们把 KV 的布局讲清楚了,可一个残酷的事实还在:KV 是模型里除权重外最肥的东西。8K 上下文 × 28 层 × 8 KV 头 × 128 维,若用 f16 存 K、V 各一份,光这一块就约 560MB——decode 每词还要把它们全扫一遍。今天把 KV 也量化成 q8,看带宽怎么压到约一半。

1. 知识点:为什么 KV 敢量化

权重能 Q4/Q8(第 5 天),KV 为什么不能?量化 KV 的顾虑是"精度",但注意两个事实:

  1. decode 只"读"KV:K/V 一旦写入就只被乘与累加,量化误差只影响当次注意力打分/加权,不像权重误差会一层层传播叠加;
  2. KV 每维都有独立的 scale:引擎对每个 (token, head) 单独定标(见下),128 维共享一个 scale,误差被锁在"一行的局部",不会累积。

于是引擎默认把内存 KV 压成 INT8(q8):每个元素从 2 字节(f16)降到 1 字节,K、V 各一份直接砍半;再配合 8-3 的"decode 扫全 KV"路径,每词的 KV 读取带宽同比例减半(另有更狠的 --kv-q4 模式,K/V 再压一半,约 1.65 倍更密,Day 24 思路同源)。

2. 对应代码:q8 KV 的存储与"一 token 一 scale"

引擎在分配阶段默认走 q8(vllm_safetensors.c 第 8208–8244 行):

/* Compressed KV cache: INT8 (near-lossless) by default; --kv-q4 switches
 * to the Q4_0 payload cache (~1.65x denser, decode dot without dequant). */
st->use_kv_q8 = g_kv_q4 ? 0 : 1;
...
st->k_cache_q8[l] = alloc_kv_blocks_i8(n_blocks, bs, kv_dim, ...);
st->k_scale[l] = cf_calloc_guard((size_t)max_seq * nkv, sizeof(float), ...); /* 每 token 每头一个 scale */

写入时按"每 token 每头"做 max-abs INT8 量化(第 9466–9476 行):

if (st->use_kv_q8) {
   
    /* Per-token per-head max-abs INT8 K/V quantization (near-lossless). */
    int8_t *kdst = kv_row_i8(st->k_cache_q8[l], cl, kv_dim, st->kv_bs);
    int8_t *vdst = kv_row_i8(st->v_cache_q8[l], cl, kv_dim, st->kv_bs);
    kv_quantize_per_head(kdst, vdst,
                         st->k_scale[l] + (size_t)cl * nkv,
                         st->v_scale[l] + (size_t)cl * nkv,
                         st->k_buf, st->v_buf, nkv, hd);
    /* Also store in float cache */  /* fp32 镜像仍保留(调试/对照用) */
    memcpy(... k_buf, kv_dim * sizeof(float)); ...
}

逐字节算笔账(2B 模型参数:nkv=8、hd=128,即每个 token 每层 K 与 V 各 kv_dim = 1024):

f16 KV:  K 1024×2B + V 1024×2B            = 4096 B / token / 层
q8  KV:  K 1024×1B + V 1024×1B = 2048 B
         + scale:8 头 × (K、V 各一) × 4B  =  64 B
         合计                              = 2112 B / token / 层
→ 4096 → 2112:缓存字节与 decode 全量扫描带宽均 ×0.52(省约 48%,接近一半)

scale 的粒度是"每个 (token, 头)"——比"整层一个 scale"细得多,这正是 q8 KV 敢自称 near-lossless 的原因(9-3 会给实测误差)。

3. 改动后果:关掉 q8 KV 试试(--kv-q4 / 全 f32 的代价)

引擎把 KV 模式做成可切:

  • 默认 q8:near-lossless,解码走 INT8 单遍(9-2 主角);
  • --kv-q4:切到 Q4_0 payload 缓存,比 q8 再密 ~1.65 倍,decode 点积免反量化直接吃 nibble;
  • 全 f32 的 KV:代码注释明确记为 DEBUG 用途(第 8210–8214 行:曾用来排查 K 缓存损坏,定位后恢复生产默认)——f32 KV 不是给生产用的,是调试探针。

想亲眼看到"KV 模式影响带宽":连跑 --bench-mixed 之外的 decode 场景不现实(无直接开关计时),但可以换位验证——8-3 说过 decode 每词扫全 KV;KV 从 4096B/token/层(f16)降到 2112B(q8)后,decode 的 KV 扫描带宽需求直接 ×0.52,这正是基准报告里 8K decode 引擎能维持 ~137ms/词(vs llama f16-KV 全扫 411ms)的重要来源之一(叠加 Day 10 的稀疏,详见 9-2/10 天)。

4. 学员调试任务

  • A 档(板端动手):
    1. 跑引擎 --kv-q4 与默认模式各一次短生成(同一 prompt),记录输出 token 是否一致(q4 KV 比 q8 更激进,肉眼可看的场景值得留个心眼);
    2. 用 5-1 的解析脚本确认模型 KV 相关张量名,结合本页算一遍"2B 8K 上下文 q8 KV 约多少 MB"(8K×28 层×2112B≈?)。
  • B 档(纯读源码):读 alloc_kv_blocks_i8/kv_quantize_per_head(在 vllm_safetensors.c 内搜索),画出"一个 token 的 K 行 → 1024 int8 + 8 个 float scale"的落盘/落存图。

预期输出:你能算清 f16→q8 KV 省多少字节、scale 为什么按"每 token 每头"、以及 f32 KV 为何只是调试探针。

收尾

  • 本篇源码点名:vllm_safetensors.c(q8 KV 分配与开关第 8208–8244 行、per-head 量化写入第 9466–9476 行)。
  • 开源仓库:Kestrel-LLM (Gitee)(AGPL-3.0-or-later 或商业许可,二选一)
  • 下篇预告:q8 KV 存好了,decode 怎么读它才最省?下一篇 9-2 进 flash_attn_single_q_q8_neon——量化 Q、在线 rescale、一条路径扫完全部 KV。
相关文章
|
12天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
7950 15
|
11天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
1753 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
11天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
1800 12
|
9天前
|
人工智能 编解码 并行计算
MiniMax-H3 一键整合包技术文档:8G 显存运行 AI 漫剧制作 —— 角色替换 / 动作迁移 / 文图生视频部署与调参指南
MiniMax H3 是 MiniMax 开源的全模态视频生成模型,支持文/图/音/视多条件输入,输出最高2K、15秒带双声道音频视频。本文档详述其Int8量化版在8GB显存下的本地一键部署、三段式工作流(EDIT/REPLACE/CONTINUE)、参数调优及常见问题排查。(239字)
|
25天前
|
人工智能 自然语言处理 安全
阿里云千问办公 QwenWork详细介绍:产品核心能力、典型场景、价格及常见问题解答
千问办公是阿里云推出的一站式AI办公平台,主打"不止于对话,更注重交付",依托通义千问旗舰大模型,用户一句话即可完成数据分析、PPT生成、视频剪辑等复杂任务,直接输出可用成果。产品深度打通钉钉生态与企业OA,覆盖桌面端、网页端,提供企业标准版198元/人/月等多档订阅方案,新用户注册即赠2000积分,适配工程师、HR、财务等多职业办公场景,成为能动手干活的"全能AI同事"。
3800 10
|
19天前
|
缓存 IDE Java
【保姆级】Android Studio下载、安装和汉化教程(2026最新)
Android Studio 是 Google 官方推出的免费 Android 应用开发集成环境,基于 IntelliJ IDEA,内置模拟器、调试器、性能分析及 Compose 界面工具,功能全面,文档丰富,是安卓开发首选工具。(239字)
2029 1

热门文章

最新文章