0.8MB 跑通 Qwen|第 17-3 篇:位级一致性实验——推理引擎两条转换路产物逐字节对拍

简介: 本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎实现,实测RK3588上位级一致性验证:同源重跑确定性、无损源跨格式殊途同归、有损源再量化边界清晰可测。覆盖Qwen3-VL多模型,强调sha256/逐张量哈希/块级比对三位一体验证。(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 篇 · 总纲(阿里云社区)

上一篇:17-2《GGUF → VQF:Q4_0…Q8_K 反量化再量化》| 下一篇:18-1《BPE 入门与 tokenizer.json》

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

一句话导读:推理引擎的位级一致性对拍实验:把所有转换产物的全文件 sha256、逐张量哈希与块级比对结果摊开,分清"确定性、无损中转一致、有损中转边界"三层各自何时成立。

关键词:手搓 Qwen 推理引擎、千问大模型推理、位级一致、逐字节对拍、sha256、safetensors、GGUF、Qwen3-VL、零依赖纯 C

导语:两条转换路都跑通之后,这篇不发明新实验,只当证物管理员:把全文件 sha256、逐张量哈希与块级比对结果一并摊开。于是"位级一致"被拆成三层来回答——同源重跑的确定性、无损中转的殊途同归,以及源已损过一轮后差异呈现的真实边界。

前两篇把两条转换路都跑通了:safetensors → A1/A3q4,GGUF(f16/Q4_0) → G16/G40。这一篇不做新实验的发明者,做证物管理员:把所有产物的全文件 sha256、逐张量哈希、块级比对结果摊在桌上,回答三个问题——① 同源两次转换能不能逐字节一致(确定性)?② 两条不同"源格式"的转换路能不能殊途同归(无损中转)?③ 一旦源已经损过一轮,差异长什么样(有损中转的边界)?

1. 知识点:位级一致的三个层次

"位级一致"要分三层说,混为一谈就会过度承诺(技术文档 §9 对"位级确定性"的表述也是分场景的):

  1. 确定性(同命令重跑):转换是纯函数——同一源码、同一命令、同一机器,两次产物理应 sha256 相同。它依赖量化/重排代码里没有隐藏的随机性或线程序依赖(定点量化无浮点求和序问题)。
  2. 跨源一致性(无损中转):safetensors(F32) 与 f16-GGUF(f16→f32 无损)携带同一份权重数值,经同一引擎量化器,产物应逐字节相同。无损中转是位级一致的充分前提。
  3. 跨源非一致性(有损中转):Q4_0-GGUF 已经丢过精度,反量化回 F32 是"有损值域还原",再量化必然与无损路径不同——差异不是 bug,是信息论。但差异不是均匀分布的:不同张量对"反量化再量化"的敏感度不同(板端实测从 100% 相同到 0.02% 相同都有)。

由此得到本系列最重要的诚实结论:位级一致是"确定性量化 + 无损源 + 同布局 flags"三条同时满足时的性质,不是"GGUF 到 VQF"的普遍保证。这正是 vqf.c 头注释红线(转换与推理共用一套量化代码)能在无损源上兑现、而在有损源上必然"部分失效"的原因。

2. 对应代码:对拍实验用什么当"尺子"

VQF 是自描述的(Day 16),所以对拍可以做到按张量名逐区域比对,而不是傻乎乎整文件 diff:

  • 目录区给出每个张量的 offset/bytes(vqf_format.h VQFTensor),两文件同名同几何的张量区域直接 memcmp;
  • 每个文件的尾部 FNV-1a(明文)覆盖"目录 + 数据区",校验和相等 ⇒ 目录与数据逐字节相同(Day 16-3 验证过算法);
  • 全文件 sha256 用 sha256sum;逐张量 sha256 用 manifest_vqf.py(零依赖,按目录 offset 分段哈希);
  • Q4_0/Q8_0 块级统计用 blkcmp(C,18B/34B 一块,数"有多少块完全一致")。

对拍对象(全部来自 Day 17-1/17-2 板端转换,RK3588 / Release 构建 / 2026-09-07):

产物 源 wmode 张量数 尺寸 (B) 文件 flags
A1 / A2 safetensors dual(默认) 49 4,159,295,496 0x63
A3q4 safetensors q4(VLLM_WMODE=q4) 41 2,331,316,232 0x63
G16 f16.gguf dual(默认) 22 3,418,562,568 0x23
G40 Q4_0.gguf dual(默认) 22 3,418,562,568 0x23
(线上)model.vqf safetensors (9 月 5 日构建,q4) 41 2,331,316,232 0x63

3. 改动后果:板端实测对拍结果

实测口径:RK3588(Orange Pi 5 Plus)/ aarch64 / Release 构建 / 2026-09-07。工具:sha256sum / manifest_vqf.py / compare_vqf.py / blkcmp(板端 /mnt/emmc/day17_tools/)。

3.1 确定性:A1 vs A2(同命令两次转换)

bc18d4d8df85c0d8164a3750ada165bd2494a02dcb67a51605dc4820de13e47f  A1.vqf
bc18d4d8df85c0d8164a3750ada165bd2494a02dcb67a51605dc4820de13e47f  A2.vqf

sha256 相等。另外 A3q4 vs 线上 model.vqf(9 月 5 日另一构建所转):

f2c168a59c8454b86ac1b9660067704b30b22ab6d910a32089607eb5f91636e6  A3q4.vqf
f2c168a59c8454b86ac1b9660067704b30b22ab6d910a32089607eb5f91636e6  .../Modl/Qwen3-VL-2B-Instruct/model.vqf
compare_vqf A3q4 model.vqf → common-equal=41 common-diff=0

跨构建、跨日期也逐字节复现(41/41 张量一致)。这坐实了 17-1 的结论:量化是纯函数,同源同 wmode 同代码 ⇒ 同一份文件。

3.2 跨源一致性:A1(safetensors)vs G16(f16-GGUF)

A1 有 49 张量(含 27 个视觉 v_*),G16 只有 22 个文本张量(GGUF 无视觉塔,17-2 的边界)。对拍共有的 22 个文本张量:

compare_vqf A1.vqf G16.vqf
common-equal=22  common-diff=0  absent=27(视觉张量仅在 A1)

22/22 全部逐字节一致。embed、5 个 norm、8 个 Q8 文本、8 个 Q4 文本,safetensors-F32 路径与 f16-GGUF 路径产出完全相同。这就是"无损中转 ⇒ 殊途同归"的实证:f16→f32 精确无舍入,两条路喂给 f32_to_q8_0/q4_0 → repack 的浮点值一模一样。

3.3 有损中转的边界:G16(f16 源)vs G40(Q4_0 源)

两个产物尺寸相同(3,418,562,568 B)、flags 相同,但逐张量哈希不同(全文件 sha:G16 bf457eee…,G40 1ff1461e…)。逐张量对比:

compare_vqf G16.vqf G40.vqf
common-equal=12  common-diff=10
不同:token_embed(F16)、q8_q/k/v/o/gate/up/down/lm(8 个 Q8)、q4_lm
相同:final/attn/ffn/q/k_norm(5 个 F32 norm)+ q4_q/k/v/o/gate/up/down(7 个 Q4)

差异不是均匀的。用 blkcmp 看块级(Q4_0 每块 18B、Q8_0 每块 34B):

q4_lm: 9723904 块 → equal=596501  diff=9127403   same=6.13%   first_diff=0
q4_q : 3670016 块 → equal=3670016  diff=0        same=100.0%
q8_lm: 9723904 块 → equal=237      diff=9723667  same=0.002%

解读(诚实、不过度归因):

  • q4_q(注意力 Q 矩阵)100% 块一致:llama.cpp 对这批矩阵的 Q4_0 量化,与引擎 f32_to_q4_0 的量化在"反量化 → 再量化"后完全闭合——块内重建值恰好落回同一量化桶。
  • q4_lm 只有 6.13% 一致:lm_head/embed 这类超宽矩阵(151936×2048)对"反量化再量化"极敏感,绝大多数块在舍入边界上翻桶。
  • q8_lm 几乎全不一致(0.002%):Q4_0 源重建的 F32 与原始 F32 的误差约 ±0.5 个量化步,被 Q8 量化器(步距更细)放大成几乎每块不同——从低精度源再量化到更高分辨率格式,差异会"稀释"到每个块里,这符合直觉:8bit 桶装不下 4bit 误差的来源信息。

诚实标注:本实验只做了"块是否一致"与全张量哈希,没有做 PPL 或生成质量对比——q4_lm 只有 6% 块一致不代表文本质量崩塌(块内差异是量化步距级的),也不代表可接受。要评估"Q4_0-GGUF 反量化再量化"对端到端输出的影响,需要独立的精度实验(Day 5-3 的 rel-err / PPL 方法论),本教程不替它下结论。另外 q4_q 与 q4_lm 的敏感度差异,原因在数值分布而非某个单一开关,这里只报实测,不做单一归因。

3.4 一条推论:manifest 就是"可复现性账本"

板端为每个产物生成了 manifest(按目录顺序逐张量 sha256 + 全文件 sha256)。它让"位级一致"的验证变成纯文本账本对比,不必让大文件同时在线——以后任何人拿到源码和源模型,都能重转并核对账本:

A3q4 manifest 尾两行:
# size=2331316232 ver=2 flags=0x063 n=41 data_off=4096 file_len=2331316224
# fullsha256 f2c168a59c8454b86ac1b9660067704b30b22ab6d910a32089607eb5f91636e6

4. 学员调试任务

  • A 档(板端动手):复刻本节三个对拍:① 同命令连续两次 --convert-vqf,sha256sum 比对;② 对 f16.gguf 与 safetensors 各转一次,用 compare_vqf.py 核对共有文本张量是否 22/22 一致;③ 对 f16 与 Q4_0 两份 GGUF 各转一次,用 blkcmp 统计 q4_lm / q4_q 的块一致率,看是否复现"6.13% / 100%"量级。
  • B 档(纯读源码):对照 vqf.c 头注释(1–11)与 vllm_safetensors.c 的 fixedpoint_quantize_saturate 注释(第 298/395 行附近),回答:① 为什么"转换与推理共用一套量化代码"是位级一致的充分条件而不仅是工程复用?② 若有人把 f32_to_q4_0 的舍入从 +8.5f 改成"四舍五入",哪些既有 VQF 文件会受影响、哪些不会(提示:重转 vs 已固化文件)?③ 技术文档 §9 为什么要把"前缀复用/spec/disk-KV"与"batch 跨 prompt"分开表述位级一致性?

预期输出:一张你自己的"产物 × sha256 × 逐张量状态"账本表,并能对着表讲清"确定性、无损一致性、有损边界"三者的成立条件。

收尾

  • 本篇源码点名:vqf.c(红线注释 1–11、双校验 485–518)、vllm_safetensors.c(量化器与 red line 注释 2916/2957/3368)、vllm_gguf.c(反量化器 319/587、层循环 822–847)、技术文档 §9 位级确定性
  • 开源仓库:Kestrel-LLM (Gitee)(AGPL-3.0-or-later 或商业许可,二选一)
  • 下篇预告:权重进内存了、格式闭环了,但推理的第一步是"文本 → token id"。Day 18 进分词器:BPE 是什么、tokenizer.json 为什么慢、引擎为什么把它编译成 vocab.bin。
相关文章
|
2天前
|
缓存 C++
0.8MB 跑通 Qwen|第 3-1 篇:推理引擎的 mmap 直挂——为什么权重加载可以只要一秒多
本文实测RK3588板端冷启动仅1.0–1.2秒,核心在于mmap直挂VQF权重文件:不全量读取,仅映射+校验头部,权重页由内核按需缺页加载,配合预量化布局,较safetensors全量读快33倍。(239字)
 0.8MB 跑通 Qwen|第 3-1 篇:推理引擎的 mmap 直挂——为什么权重加载可以只要一秒多
|
3天前
|
编译器 C语言
0.8MB 跑通 Qwen|第 2-1 篇:推理引擎的 C11 `_Static_assert`——让编译器守卫你的内存布局
本文详解C11 `_Static_assert` 在内存布局守卫中的关键作用:针对mmap直挂场景,通过编译期断言钉死VQF格式三结构体(200/432/64字节),杜绝因对齐差异导致的静默错位。真机RK3588实测验证,实现错误前移。
0.8MB 跑通 Qwen|第 2-1 篇:推理引擎的 C11 `_Static_assert`——让编译器守卫你的内存布局
|
1天前
|
编译器 C语言
0.8MB 跑通 Qwen|第 6-1 篇:推理引擎的 ARMv8.2 dotprod——`vdotq_s32` 一条指令做 4 个点积
本系列《0.8MB跑通Qwen》聚焦ARM零依赖纯C推理引擎,实测RK3588平台,适配Qwen3-VL多模型。本文详解ARMv8.2 dotprod指令(vdotq_s32)如何在真实大内核中释放性能,破除微基准误导,揭示寄存器压力下加速本质。(239字)
|
1天前
|
调度 C++
0.8MB 跑通 Qwen|第 14-2 篇:单 SSE 流与串行队列——推理引擎的"单机守则"
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,适配Qwen3-VL多尺寸模型(2B/8B/30B),基于RK3588实测。本文详解“单机守则”:推理状态串行锁定、SSE流独立输出、HTTP与推理线程职责分离,揭示为何默认串行是稳定前提而非性能缺陷。(239字)
|
1天前
|
调度 C++
0.8MB 跑通 Qwen|第 7-3 篇:推理引擎的预热与测量抖动——为什么报告要"重复 3 次取中位"
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎开发,实测适配Qwen3-VL多模型,在RK3588上验证性能抖动——单内核耗时波动达1.7倍,确立“预热+3次取中位”为可信基准口径。(239字)
|
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。
|
1天前
|
调度 C++ 索引
0.8MB 跑通 Qwen|第 12-3 篇:复现 13.1×——推理引擎的跨进程恢复实测怎么做(含口径拆解)
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,实测RK3588平台运行Qwen3-VL多模型;详解跨进程KV恢复机制,揭示prefill/TTFT/wall三口径差异,13.1×加速比可复现、可验证。(239字)
|
1天前
|
并行计算 NoSQL Java
0.8MB 跑通 Qwen|第 4-2 篇:推理引擎的 vllm_tp_parfor——调用线程也干活
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,实测RK3588平台高效运行Qwen3-VL多模型。本文深入`vllm_tp_parfor`,揭示调用线程主动领任务、静态切块与分级收尾机制,以计数器实验确证“每核皆算力”,展现极致轻量与工程严谨的统一。(239字)
|
1天前
|
缓存 自然语言处理 安全
0.8MB 跑通 Qwen|第 11-1 篇:大模型推理中多轮对话的浪费——上一轮 prefill 算完就被扔了
本系列《0.8MB跑通Qwen》聚焦ARM端零依赖纯C推理引擎,实测RK3588平台高效运行Qwen3-VL多模态模型;核心突破是默认启用前缀KV复用,使多轮对话prefill耗时骤降27倍,显著消除历史重复计算,大幅提升端侧长会话效率。(239字)
|
1天前
|
缓存 内存技术
0.8MB 跑通 Qwen|第 9-2 篇:推理引擎的 flash_attn_single_q_q8_neon——单 q 单遍扫全 KV
本系列《0.8MB跑通Qwen》聚焦ARM零依赖纯C推理引擎,精读`flash_attn_single_q_q8_neon`内核:通过Q单次量化、q8 KV单遍扫描、在线softmax与尾部归一,显著降低带宽与内存开销,实现在RK3588等端侧设备高效运行Qwen3-VL系列模型。(239字)

热门文章

最新文章