0.8MB 跑通 Qwen|第 6-2 篇:推理引擎的 8x8 布局重排——把"取数"提前到转换期

简介: 本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,适配Qwen3-VL多模态模型,在RK3588芯片实测落地。核心创新是Q8权重的8×8内存重排,使宽矩阵GEMM提速约1.7倍,且位级结果一致,兼顾性能与精度。(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 篇 · 总纲(阿里云社区)

上一篇:6-1《ARMv8.2 dotprod:vdotq_s32 一条指令做 4 个点积》 | 下一篇:6-3《4x4 asm 内核:从 llama.cpp 提取的 MIT 代码》

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

一句话导读:推理引擎的 8x8 布局重排:把权重的排队方式在转换期改成 8 行交织布局,讲清它为何让宽矩阵 GEMM 快约 1.7 倍、窄 QKV 不受影响,且两种布局位级一致。

关键词:手搓 Qwen 推理引擎、千问大模型推理、8x8 布局、权重重排、GEMM、Q8 量化、RK3588、Qwen3-VL、零依赖纯 C

导语:GEMM 快不快,一半取决于权重在内存里怎么排队。本篇讲引擎的 8x8 布局重排:转换期把行优先权重改成 8 行交织,让内核一次突发读就把整块喂给 vdot。--bench-mixed 实测——宽矩阵快约 1.7 倍、窄 QKV 不受影响,且两种布局位级一致。

6-1 里那条"微基准会骗人"的伏笔,今天兑现:GEMM 快不快,一半取决于权重在内存里怎么排队。引擎把权重的排队方式(布局)当作一等公民——在文件转换期就重排好,运行期只负责"按排好的顺序读"。

1. 知识点:为什么"天然布局"喂不动 vdot

naive 的 Q8_0 权重是"一行一行存":{scale0, qs[32]} {scale1, qs[32]} ...(每行 32 个元素一块)。但 vdot 一次吃 16 个 int8,要算"8 行 × 8 列"的块,需要同时拿到8 行同一列的 16 个元素——按行存的话,这 16 个元素散在 8 个不同的块里,每个都要一次独立的 cache-line 访问。

解法是重排(repack):转换模型时把权重从"行优先"变成"8 行 × 32 列为一组、8 行的第 k 段排在一起"的交织布局。这样内核运行时,8 行的数据是连续相邻的——一次突发读就把一整块喂给 vdot。重排只做一次(转换期),换来的却是每次推理都不再付"随机取数"的代价。

引擎里这个 8x8 布局的注释写得很清楚(vllm_safetensors.c 第 3408–3413 行):

/* Q8_0 8x8 tiled layout (block_q8_0x8, 272 B/block): P0 tile upgrade.
 *   { d[8] f16; qs[256] }, qs[k*32 + m*4 + i] = row m's qs[k*4+i].
 * Same per-output int32/fp32 accumulation order as the 4x4 kernel =>
 * results are bit-identical (verified on board 2026-08-29, 1.6x GEMM /
 * 1.17x GEMV). Gate: VLLM_Q8_8X8=0 falls back to the 4x4 layout.
 */

static int g_q8_8x8 = -1;
static int q8_8x8_enabled(void) {
   
    if (g_q8_8x8 < 0) {
   
        const char *e = getenv("VLLM_Q8_8X8");
        g_q8_8x8 = (e && e[0] == '0') ? 0 : 1;   /* default 8x8 */
    }
    return g_q8_8x8;
}

两个值得记的点:

  1. "位级一致"是布局切换的前提:8x8 与 4x4 的累加顺序设计成相同,所以换布局不改结果(注释里写了"bit-identical, verified on board")——布局是纯性能开关,不是精度开关;
  2. 有开关就有退路:VLLM_Q8_8X8=0 让引擎退回 4x4 布局。第 3 节的实验就靠它。

重排函数 repack_q8_0_8x8_inplace(第 3426 行起)在内存里做原位置换,把 legacy 的 [rows][nb*34] 变成 8x8 的 272 B/block——它跑在模型转换期,每个权重只重排一次。

2. 对应代码:内核怎么"消费"重排后的布局

gemm_q8_0_8x8_neon(第 4418 行起)按 8 行一组推进,每组读一个 272 B 的块,8 个 f16 scale 一次取齐、256 个 int8 连续喂给 vdot——没有一条指令是在"找数据"。这正是 5-1 的教训在代码层的落地:紧凑 + 连续 = 有效带宽最大化。

3. 改动后果:关掉 8x8 重排,看宽矩阵内核慢多少

引擎内置的 --bench-mixed 用同一份合成 Q8 权重分别以 8x8(默认)与 VLLM_Q8_8X8=0(退回 4x4 布局)跑同一组 decode 内核。板端实测(RK3588 / 2026-09):

--- 默认(8x8 布局) ---
Q8 fused QKV (dec)         1.641 ms   30.7 GFLOPS
Q8 fused gate+up (dec)     3.922 ms   51.3 GFLOPS
Q8 fused down+res (dec)    1.988 ms   50.6 GFLOPS

--- VLLM_Q8_8X8=0(退回 4x4 布局) ---
Q8 fused QKV (dec)         1.592 ms   31.6 GFLOPS
Q8 fused gate+up (dec)     6.660 ms   30.2 GFLOPS
Q8 fused down+res (dec)    3.361 ms   30.0 GFLOPS

读数字要分形状:

  • gate+up / down+res(宽矩阵,行数远多于 QKV):8x8 比 4x4 快 约 1.7×(51.3 vs 30.2、50.6 vs 30.0 GFLOPS)——宽矩阵里"取数连贯性"就是命根子,布局重排的收益在这显形(与代码注释"1.6x GEMM"同量级);
  • QKV(窄矩阵):两者几乎一样(30.7 vs 31.6)——窄矩阵的瓶颈不在取权重,布局救不了它。

这就是"把取数提前到转换期"的完整证据:同样的指令、同样的数据量,只是权重排队方式不同,宽内核差 1.7 倍;而且这不是以牺牲正确性换的——两种布局输出位级一致(代码注释背书)。

动手验证姿势:VLLM_Q8_8X8=0 ./build-rk3588/vllm_kestrel --bench-mixed | grep "Q8 fused",与默认跑一次对比 gate+up 那两行。

4. 学员调试任务

  • A 档(板端动手):复现第 3 节——默认与 VLLM_Q8_8X8=0 各跑一次 --bench-mixed,记录三对 Q8 fused 数字与 GFLOPS,说出哪些内核受布局影响、哪些不受。
  • B 档(纯读源码):读 repack_q8_0_8x8_inplace(第 3426 行起),画出"8 行 legacy 34B/块 → 272B 8x8 块"的字节搬运图(哪 8 个 f16 放前面、256 个 int8 怎么交织)。

预期输出:你能解释"8x8 重排为什么让宽矩阵内核快 1.7×、而 QKV 不受影响",并理解"布局与正确性解耦(位级一致)"这个工程选择的意义。

收尾

  • 本篇源码点名:vllm_safetensors.c(8x8 布局注释与 q8_8x8_enabled 第 3408–3422 行、repack_q8_0_8x8_inplace 第 3426 行、gemm_q8_0_8x8_neon 第 4418 行)。
  • 开源仓库:Kestrel-LLM (Gitee)(AGPL-3.0-or-later 或商业许可,二选一)
  • 下篇预告:8x8 是引擎自己排的;可 x86/llama 侧还有一种更狠的 4x4 手写 asm——引擎直接"提取"了它。下一篇 6-3 讲提取第三方内核的工程与许可边界。
相关文章
|
1天前
|
存储 缓存 编解码
0.8MB 跑通 Qwen|第 19-2 篇:推理引擎的 vision_tokens 客户端预编码协议
本系列《0.8MB跑通Qwen》专注手搓零依赖纯C推理引擎,适配Qwen3-VL多模态大模型,在RK3588(aarch64)实测通过。本文详解`encode_media_item`三路协议:`image_url`(板端ViT+128位缓存)、`video_frames`(帧序列)与`vision_tokens`(客户端预编码跳过ViT),支持字节数自校验与缓存命中优化,真机实测ViT耗时295ms→缓存后仅301ms。
|
2天前
|
定位技术 Python
0.8MB 跑通 Qwen|第 3-2 篇:推理引擎的字节序与十六进制纪律——一个字节序错误,引擎当场翻脸
本文以RK3588真机实测为基础,深入剖析字节序陷阱:揭示VQF格式中小端落盘导致魔数“VQFW”在磁盘呈现为“FQFW”,并用篡改version字段的实验直观展示——小端机器误读大端数据会直接拒载(报错33554432≠2)。强调“先问字节序,再读数字”的十六进制读法铁律。(239字)
0.8MB 跑通 Qwen|第 3-2 篇:推理引擎的字节序与十六进制纪律——一个字节序错误,引擎当场翻脸
|
1天前
|
缓存 C语言 C++
0.8MB 跑通 Qwen|第 9-3 篇:推理引擎的 q8 KV 精度对照——量化进注意力,输出差多少
本系列《0.8MB跑通Qwen》手搓零依赖纯C推理引擎,适配Qwen3-VL多尺寸模型,在RK3588上实测q8 KV量化:输出误差仅≈0.01,端到端PPL损失<1%,带宽降为52%,精度与效率达成教科书级平衡。(239字)
|
1天前
|
缓存 安全 API
0.8MB 跑通 Qwen|第 11-2 篇:大模型推理的前缀缓存键——怎么知道"两轮一样"?
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,实测RK3588上高效运行Qwen3-VL多模态模型。本文详解前缀缓存核心机制:以token序列最长公共前缀(LCP)为键,实现KV复用;并揭示“完全相同反不复用”的安全设计——确保prefill刷新logits,杜绝空响应。
|
1天前
|
编解码 应用服务中间件 API
# 0.8MB 跑通 Qwen|第 19-3 篇:视频帧与媒体模块——推理引擎默认不启用的 H.264 独立模块
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,适配Qwen3-VL多模态模型(2B/8B/30B),在RK3588上实测。视频支持分两层:帧序列路径已落地;MP4/H.264解码为独立未完成模块,API清晰、默认不构建、不演示未验证功能,体现严谨工程边界。(239字)
|
1天前
0.8MB 跑通 Qwen|第 13-1 篇:推理引擎的 decode 为什么慢——逐词、带宽、不可并行
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,适配Qwen3-VL多模态模型,在RK3588上实测decode瓶颈——逐词生成、内存带宽受限、无法并行。直击每词92ms硬地板,为推测解码(speculative decode)铺路,实现“一次前向多产出”。
|
1天前
|
NoSQL 调度 C++
0.8MB 跑通 Qwen|第 14-3 篇:推理引擎的批处理正确性边界——margin 0.71 vs 0.032
本篇精读Qwen3-VL系列推理引擎的“位级一致性”本质:批量与串行输出大多逐字节一致,但因浮点累加顺序差异,在near-tie(近平局)场景下存在翻盘风险——margin(如0.71 vs 0.032)决定是否一致。这是工程结果,非数学承诺。
|
1天前
|
Java Linux 调度
0.8MB 跑通 Qwen|第 4-3 篇:核绑定实验——推理引擎在 ARM 大小核上为什么叮嘱"勿设 8 线程"
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,实测RK3588(4×A76+4×A55)上适配Qwen3-VL-2B/8B及30B-A3B模型。通过三组真机实验揭示“核多≠快”本质:仅绑4大核最优,设8线程反降效12%,验证大小核架构下线程配置需严守硬件特性。(239字)
|
1天前
|
缓存
0.8MB 跑通 Qwen|第 8-1 篇:大模型推理的自注意力数学——Q·K^T / softmax / V
本文精讲自注意力三步核心:Q·K^T打分、softmax归一化、加权V求和,逐行对照纯C引擎源码(vllm_transformer.c),剖析√d缩放、因果掩码与数值稳定性等工程地雷,聚焦Qwen3-VL系列在RK3588上的零依赖推理实现。(239字)
|
1天前
|
JSON C语言 数据安全/隐私保护
0.8MB 跑通 Qwen|第 16-1 篇:VQF 的野心——推理引擎如何把量化与布局固化到文件
本系列手搓零依赖纯C推理引擎,仅0.8MB即可在RK3588上跑通Qwen3-VL多模态大模型。核心创新VQF格式将量化类型、内核布局与模型属性固化于2.17GB单文件中,实现mmap直挂、零转换加载,真正达成“加载即用”。

热门文章

最新文章