自回归大模型通用推理流程,不绑定GGUF、不绑定vLLM,权重文件只参与模型加载步骤。
# ==================阶段A:加载,访问GGUF磁盘==================
# 打开磁盘GGUF文件
gguf_fd = open("model-q4_k_m.gguf","rb")
# 1 读取头部校验
header = read_gguf_header(gguf_fd)
# 2 读取元数据,拿到架构、tokenizer
model_meta = read_gguf_metadata(gguf_fd)
# 3 读取张量索引,拿到每个权重 offset、qtype
tensor_list = read_gguf_tensor_info(gguf_fd)
# 4 根据offset读取权重二进制,mmap或者read拷贝
runtime_weight_buf = mmap_gguf_weights(gguf_fd, tensor_list)
# 构建运行时模型对象:把qtype、维度全部存入内存,不再依赖磁盘
runtime_model = create_runtime_model(model_meta, tensor_list, runtime_weight_buf)
# GGUF磁盘文件任务完成,可以关闭
gguf_fd.close()
# ==================阶段B:推理循环,完全不碰GGUF磁盘==================
tokenizer = runtime_model.tokenizer
input_ids = tokenizer.encode("你好,请介绍GGUF")
while not is_eos(input_ids):
# 关键点:使用内存里保存的 qtype,对内存中权重实时反量化
logits = transformer_forward(runtime_model.weights, runtime_model.qtype_table, input_ids)
next_token = sample(logits)
input_ids.append(next_token)
output_text = tokenizer.decode(input_ids)
print(output_text)
llama.cpp行为:解析GGUF取出元数据、qtype、offset;mmap建立虚拟内存映射,不一次性全部载入物理内存;算子访问权重发生缺页,操作系统把磁盘数据载入RAM;推理时读取内存中qtype,CPU/GPU算子完成反量化。
vLLM行为:GGUF仅作为磁盘数据源;解析得到元数据、qtype;不使用mmap,一次性读入显存;GPU算子读取显存内qtype标记完成反量化推理。
| 对比维度 | llama.cpp + GGUF | vLLM + GGUF |
|---|---|---|
| GGUF磁盘访问时机 | 仅模型加载阶段访问 | 仅模型加载阶段访问 |
| 量化信息 qtype来源 | GGUF的TensorInfo,加载存入内存 | GGUF的TensorInfo,加载存入内存 |
| 加载实现 | mmap缺页按需加载 | 全量读入显存,无mmap |
| 反量化执行位置 | ggml算子,CPU/GPU均可 | CUDA算子,GPU内完成 |
| KV‑Cache实现 | 普通缓冲区,串行推理 | PagedAttention分页KV,高并发批处理 |
| 硬件支持 | CPU / Metal / CUDA / AMD | 强依赖NVIDIA CUDA |
GGUF是静态二进制存储格式,本身不具备任何计算能力;全部反量化、矩阵运算、推理逻辑由llama.cpp/vLLM这类引擎实现。下面列出GGUF内部每一块内容,并且明确每一块在推理流程中承担的角色。
typedef struct {
uint32_t magic; // 魔数固定为"GGUF"
uint32_t version; // 文件版本,主流v3
uint64_t n_tensors; // 文件中张量总数量
uint64_t n_metadata; // 元数据键‑值对数量
} gguf_header_t;
| 元数据Key | 在推理流程中的作用 |
|---|---|
| general.architecture | 识别模型架构,选择对应推理代码路径 |
| llama.context_length | 初始化KV‑Cache最大上下文窗口 |
| llama.block_count | 构建Transformer网络层数 |
| llama.embedding_length | Embedding向量维度 |
| tokenizer.model | SentencePiece二进制,编码Prompt、解码输出文本 |
| general.alignment | 权重内存对齐字节,优化算子性能 |
typedef struct {
gguf_string_t name; // 权重名字,如 blk.0.attn_q.weight
gguf_quant_type type; // qtype:Q4_K_M / Q8_0 / FP16,反量化的依据
uint32_t n_dims; // 张量维度数目
uint64_t dims[4]; // 张量各个维度大小
uint64_t offset; // 当前张量在GGUF文件内的字节偏移
} gguf_tensor_info_t;
| 量化类型 | 等效比特 | 推理运行时行为 |
|---|---|---|
| FP16 | 16bit | 不需要反量化,直接矩阵运算 |
| Q8_0 | 8bit | 轻量反量化,速度快,精度损失很小 |
| Q5_K_M | ~5bit | K‑Quant高质量档位,反量化开销中等 |
| Q4_K_M | ~4bit | 综合平衡,本地推理最常用 |
| Q3_K_M | ~3bit | 高压缩,反量化开销上升,质量下降 |
| Q2_K | ~2bit | 极限压缩,反量化开销大,质量较差 |
import struct
def read_gguf_header(path):
with open(path,"rb") as f:
magic = f.read(4)
version = struct.unpack("<I", f.read(4))[0]
n_tensors = struct.unpack("<Q", f.read(8))[0]
n_meta = struct.unpack("<Q", f.read(8))[0]
print(f"magic:{magic}, ver:{version}, tensors:{n_tensors}, meta:{n_meta}")
# read_gguf_header("model.q4_k_m.gguf")
前 3 章讲清楚了 GGUF 是通用容器协议, llama.cpp 是通用推理引擎; 但 Bonsai 是个不通用的模型 — 它必须拉 llama.cpp 的分支 (prism) 才能跑。这一章讲: 标准 GGUF 量化 (PTQ) 为何不够用, Bonsai 拉分支的 4 个定制点是什么, 每个定制点按 场景 → 说明 → 流程 → 源码 四件套讲。
| 术语 | 通俗解释 | 出现位置 |
|---|---|---|
| blck_size | 一个量化 block 含多少个原始 weight。Q4_0 是 32, Q4_K 是 256, Q1_0 是 128, Q2_0 是 256 | ggml.c type_traits |
| vec_dot | 矩阵乘的核心: W · x, 但 W 是量化的,x 是 Q8_0/Q8_1 量化激活 → 输出 FP32 | ggml-quants.c |
| type_traits | 每种 ggml_type 对应的元数据 + 函数指针表 (dequantize/quantize/vec_dot) | ggml.c:929 |
| __byte_perm | CUDA SIMD 指令: 32-bit 字内 4 个 byte 按位重排, 一次 SIMD 算 4 个查表 | CUDA Q2_0 vec_dot |
| dp4a | CUDA DP4A 指令: 4 个 int8 dot product 一次, 输出 int32 (Hopper/Ada 架构) | CUDA Q2_0 vec_dot |
| STE (直通估计) | 训练 trick: 量化函数不可微, 但反向梯度当 1 通过, 假装量化是恒等映射 | Bonsai QAT 训练 |
| per-group scale | 每 N 个 weight 共享一个 FP16 scale, 解决 LLM 0.1% outlier 通道精度问题 | Q1_0/Q2_0 都用 |
| block_q2_0 | 单个 Q2_0 block: FP16 d (2B) + 64B packed 2-bit, 共 66 字节, 含 256 weight | ggml-quants.c |
| Q8_0 / Q8_1 | 激活量化格式: Q8_0 是每 32 个激活共享一个 scale, Q8_1 是每 32 个共享一个 scale + 块级额外 scale | vec_dot 配对用 |
| MMQ vs MMVQ | CUDA 端两种矩阵乘路径: MMQ (matrix-matrix quantized, 大批量) + MMVQ (matrix-vector quantized, 单 token) | ggml-cuda |
这是理解 Bonsai 拉分支的钥匙。如果跳过这一节, 后面的定制点和洞察都会看不懂。
PTQ 的特点:
QAT 的特点:
| 量化方法 | 类型 | 典型精度 (vs FP16) | 训练需要? |
|---|---|---|---|
| F16 / Q8_0 | PTQ | 99~100% | 否 |
| Q4_K_M (主流) | PTQ | 92~95% | 否 |
| Q3_K | PTQ | 85~90% | 否 |
| Q2_K | PTQ | 70~80% | 否 |
| 朴素 1-bit | PTQ | < 50% (崩) | 否 |
| Bonsai 1-bit (Q1_0) | QAT + 蒸馏 | 90% | 是 (重训) |
| Bonsai ternary (Q2_0) | QAT + 蒸馏 | 95% | 是 (重训) |
GGUF 存储的是量化后的字节流, 不是 FP32 权重。推理算子 (gemm) 只能对 FP32 计算, 所以必须在算之前把量化字节 还原 成 FP32, 才能进入 Linear 层。
朴素做法: 把每个 weight 都存成 FP32, 27B 模型就是 54 GB。Bonsai 三值做法: 存成 {-1, 0, +1} + 一个 FP16 scale, 256 weights 占 66 字节 (vs FP32 1024 字节, 省 15×)。
每个 weight 需要 2 bit, 4 种编码:
| 2-bit 编码 | 还原符号 | 出现频率 | 含义 |
|---|---|---|---|
00 | -1 | ~25% | 负权 |
01 | 0 | ~40-50% | 稀疏! 后续点积直接跳过 |
10 | +1 | ~25% | 正权 |
11 | (保留) | 0% | 故意不用 — 只用 3 种编码,留 1 个未来扩展 |
为什么需要 FP16 scale? 如果只用 {-1, 0, +1} (绝对值都是 1), 模型表达能力太弱 (相当于 binary sign), 只能存"哪些连接重要/不重要"。加上 per-group FP16 scale, 让 {-1, 0, +1} 乘以一个动态范围, 就能表达"这个 group 里权重绝对值大" — 这是 LLM 0.1% outlier 通道的关键。
void dequantize_row_q2_0(const block_q2_0 * x, float * y, int64_t k) {
static const int qk = QK2_0; // blck_size = 256
const int nb = k / qk; // k 个 weight 分成 nb 个 block
for (int i = 0; i < nb; i++) {
// 1. 读 FP16 scale, 转 FP32
const float d = GGML_FP16_TO_FP32(x[i].d);
// 2. 遍历 block 内 256 个 weight
for (int j = 0; j < qk; ++j) {
// 3. 计算这个 weight 在 packed 字节流里的位置
// 每 4 个 weight 共享 1 byte (各占 2 bit)
const int byte_index = j / 4;
const int bit_offset = (j % 4) * 2;
// 4. 提取 2-bit 编码
const uint8_t q = (x[i].qs[byte_index] >> bit_offset) & 0x03;
// 5. 查表 + 乘 scale (00=-1, 01=0, 10=+1, 11=+2)
y[i*qk + j] = ((int)q - 1) * d;
}
}
}
解读:
qk = 256: block 内含 256 个 weight, 这是 blck_size 字段x[i].d: 这个 block 共享的 FP16 scale, 所有 weight 还原时都用同一个 dj / 4: 每 4 个 weight 共享 1 byte (4×2bit = 8bit = 1B)(j % 4) * 2: 这个 weight 在 byte 内的位偏移 (0/2/4/6)& 0x03: 提取低 2 bit, 得到 0~3 的编码(int)q - 1: 编码 0→-1, 1→0, 2→+1, 3→+2 (注意 11=+2, 跟上面"保留"的说法略有差异, 但效果一致)* d: 乘以 group scale, 还原 FP32 实际值// Q4_0: 4-bit 编码, 范围 [-8, 7], 不存 min
y[i*qk + j] = ((int)(x[i].qs[j/2] >> ((j%2)*4) & 0x0F) - 8) * d;
// 区别: Q4_0 是 4-bit, 16 个值均匀分布; Q2_0 是 2-bit, 只有 3 个有效值
// Q2_0 编码更稀疏 (0 值多), 但 scale 范围更大
// 模板实例化, 把 Q2_0 的 256 个 weight 反量化成 FP32
template <int QK, int QR, void (*dequantize_func)(const void*, const int64_t, const int, float2&)>
static __global__ void dequantize_block_cont_cuda(...);
// 在 Q2_0 的 case 里这样实例化:
return dequantize_block_cont_cuda<QK2_0, QR2_0, dequantize_q2_0>;
// 其中 dequantize_q2_0 在 dequantize.cuh:25
static __device__ __forceinline__ void dequantize_q2_0(
const void * vx, const int64_t ib, const int iqs, float2 & v) {
const block_q2_0 * x = (const block_q2_0 *) vx;
const float d = x[ib].d; // FP16 scale
const uint32_t q = *(const uint32_t*)(x[ib].qs + iqs * 4); // 一次读 4 个 int16 = 8 个 weight
// ... 用位运算提取 4 个 2-bit 编码, 查表还原成 FP32
}
为什么选 {-1, 0, +1} 而不是 {-2, -1, 0, +1, +2}?
三值 (2.0625 bpw) 比四值 (2.0 bpw) bit 多, 但 多出来的 0 值天然稀疏 — 实测 LLM 权重 ~40% 是 0, 这些 0 在点积时直接跳过, 速度反而更快。
反直觉: bit 多一点 → 更快。
GGUF 文件格式允许私有 type id, Bonsai Q2_0 的 GGUF 文件本身合法。但 推理引擎必须实现 dequantize + vec_dot 才能跑。所以下游引擎不实现 Q2_0 case, 就只能看不能跑 — "unsupported tensor type Q2_0"。
dequantize_row_q2_0 把 256 个 weight 还原成 FP32, 但这只是 准备数据。真正的算子是 点积: Y = sum(w[i] * x[i]), 256 次乘加。如果用朴素 FP32 算, 跟没量化一样慢 (甚至更慢, 因为多了一次反量化)。
真正的优化: 跳过反量化, 直接算 packed × Q8_1 点积。
输入:
公式:
dot = d_w * Σ_i (q_w[i] - 1) * q_x[i]
= d_w * [ Σ_i q_w[i] * q_x[i] - Σ_i q_x[i] ]
关键: Σ_i q_x[i] 是 activation 的总和, 可以预计算 (扫一次 X 即可)。剩下要算的就是 Σ_i q_w[i] * q_x[i]。
// Device 内联函数, 一个 warp 处理 32 个 vocab 中的一个 chunk
static __device__ __forceinline__ float vec_dot_q2_0_q8_1(
const void * vbq, const block_q8_1 * bq8_1, const int & kbx, const int & iqs) {
const block_q2_0 * bq2_0 = (const block_q2_0 *) vbq + kbx;
const float d2 = bq2_0->d; // FP16 scale
const int16_t * qs = (const int16_t *) bq2_0->qs + iqs * 4; // 每次取 4 个 int16 (8 个 weight × 4 个 chunk)
const block_q8_1 * bq8_1_chunk = bq8_1 + iqs;
int sumi = 0;
#pragma unroll
for (int j = 0; j < 4; ++j) {
const int q = qs[j]; // 一个 int16 含 4 个 2-bit 编码
const int u = get_int_b4(bq8_1_chunk->qs, j*2+0); // 4 个 int8 activation 之一
const int v = get_int_b4(bq8_1_chunk->qs, j*2+1); // 4 个 int8 activation 之二
// 关键魔法: 0x020100FF 字节序列 = {-1, 0, 1, 2}
// 用 __byte_perm 一次 SIMD 把 4 个 2-bit 编码查表还原成 4 个字节
const int qe = __byte_perm(0x020100FF, 0x020100FF, q >> 0); // 低 2 位查表
const int qo = __byte_perm(0x020100FF, 0x020100FF, q >> 2); // 次低 2 位查表
// 把 qe/qo 重新排列成 qx (低 4 个) / qy (高 4 个)
const int qx = __byte_perm(qe, qo, 0x5140);
const int qy = __byte_perm(qe, qo, 0x7362);
// dp4a: 4 个 int8 dot product, 输出 int32, 一次算完
sumi = ggml_cuda_dp4a(u, qx, sumi); // dot product accumulate 4×4 int8
sumi = ggml_cuda_dp4a(v, qy, sumi);
}
return d2 * __low2float(sumi); // 标量乘 d, 转 FP32
}
解读 4 个关键点:
// 注意: Q1_0 / Q2_0 / Q4_0 / Q4_1 ... 共用同一 case 体
case GGML_TYPE_Q1_0:
case GGML_TYPE_Q2_0:
case GGML_TYPE_Q4_0:
// 通用路径: 先 dequantize 成 FP32, 再用 FP32 gemm
// Q2_0 没有 CPU 专用 vec_dot (不像 CUDA 那样优化到极致)
{
// ... dequantize_row_q2_0 + cblas_sgemm
}
break;
解读: CPU 端没有为 Q2_0 写专用 SIMD 内核, 而是 dequantize → FP32 gemm。这意味着 CPU 上跑 Q2_0 不会有 7.8× 加速, 只比 FP16 略快 (因为内存带宽省了)。
洞察: Bonsai 的 1.7 bit 加速主要是 GPU 上的, CPU 上优势有限。
27B 即使 1-bit 也要 ~6ms/步, 生成 1000 token ≈ 6 秒。这是 自回归 的代价 — 每生成一个 token 都要跑一次完整 forward。
DSpark 用一个 极小 drafter 一次性生成 K 个候选 token, 主模型 1 次 forward 验证整块, 接受 m 个 (m 接近 K 时加速 ≈ K/m 倍)。
confidence_head: 判断每个 draft 的质量, 决定是否继续markov_head (可选): 对 draft 重采样, CUDA 加速// Device-side dspark vanilla Markov resample. Self-contained:
// depends only on the CUDA runtime, not on ggml/llama internals.
#include "dspark-markov.h"
#include <cuda_runtime.h>
#include <math_constants.h>
#define DSPARK_WARP 32 // 一个 warp 32 线程, 处理 32 个 vocab
#define DSPARK_WARPS_PER_BLOCK 8 // 一个 block 8 个 warp = 256 线程
#define DSPARK_BLK_THREADS (DSPARK_WARP * DSPARK_WARPS_PER_BLOCK) // 256
#define DSPARK_NBLOCKS 1024 // grid-stride partial-argmax 网格宽
// Fused GEMV + add-base + per-block partial argmax for one position k.
// 把 matrix-vector 乘 + 加 base logits + 找 argmax 三步融合成一个 CUDA kernel
struct dspark_markov_cuda {
int64_t n_vocab = 0; // vocab 大小 (典型 152064)
int64_t rank = 0; // markov_rank (典型 64)
float * d_w1 = nullptr; // [n_vocab * rank] GPU 权重 1
float * d_w2 = nullptr; // [n_vocab * rank] GPU 权重 2
float * d_base = nullptr; // [base_cap_rows * n_vocab] base logits 缓存
int32_t * d_out = nullptr; // [base_cap_rows] 输出 token id
int32_t * d_prev = nullptr; // 链式 prev token (1 int)
cudaStream_t stream = nullptr;
};
解读:
大多数 PTQ 方案 (GPTQ/AWQ/SmoothQuant) 都会保留 部分层在 FP16:
结果: Q4_K 看起来 18 GB, 实际能砍到 ~12 GB, 但还有 ~6 GB 浪费在 FP16 部分。
Bonsai 公告: "low-bit representation runs end to end across the language network, embeddings, attention, MLPs, and the LM head, with no higher-precision escape hatches"。
翻译: embedding/LM head 也量化, 一个 FP16 都不留。
为什么 1-bit 模型推理比 FP16 快 5~10×? 加速来自 3 个独立机制:
具体例子: [seq=512, hidden=5120] @ [5120, 5120] 这一个 Linear 层:
量化如何打破 memory wall:
| 位宽 | 权重大小 | 读取时间 (RTX 5090) | 加速比 |
|---|---|---|---|
| F16 | 52 MB | 29 μs | 1× |
| Q4_K | ~15 MB | 8.3 μs | 3.5× |
| Q2_0 | ~6.6 MB | 3.7 μs | 7.8× |
| Q1_0 | ~3.6 MB | 2.0 μs | 14.5× |
关键: 1-bit matvec 没有浮点乘法。现代 CPU/GPU 提供:
VPOPCNTDQ (AVX-512) — 一次算 8 个 64-bit popcountvcnt (NEON / ARM) — 一次算 8 个 8-bit popcount__popcnt64 (x86) — 64-bit popcountvote.ballot — 32 线程协作 popcountLLM 权重 ~30~50% ≈ 0, 阈值化后形成 2:4 结构化稀疏, GPU 可以 2× speedup。Q2_0 反量化点积: 0 值跳过乘加, 等价 ALU workload -40%。
| 度量 | F16 | Ternary | 差异 |
|---|---|---|---|
| 比特密度 (bpw) | 16 | 1.71 | 9.4× 更少 |
| 信息密度 (有效 bits/weight) | ~10 | ~1.5 | 6.7× 更少 |
| 模型能力 | 100% | 95% | 仅损失 5% |
| 智能密度 (cap/GB) | ~0.05 | ~0.16 | 3.2× 更高 |
| 层 | 技术 | 贡献 |
|---|---|---|
| 1. 物理层 | 1.125 bpw 内存占用 | 13× 带宽压缩 |
| 2. SIMD 层 | popcnt + 位运算 | 5× ALU 加速 |
| 3. Sparsity 层 | Ternary 30~50% 零 | 1.5× 加速 (仅 ternary) |
| 4. Cache 层 | 小权重进 L2/L3 | 命中率↑ |
| 5. 调度层 | DSpark 投机解码 | 2~3× 加速 (lossless) |
5 层叠加, 才有 Bonsai 1-bit 27B 在 RTX 5090 上 163 tok/s 的成绩 (相对 FP16 27B ~10 tok/s 加速 ~16×)。
| 方案 | 何时量化 | 核心机制 | 典型 bpw | 精度 | 代表规模 |
|---|---|---|---|---|---|
| GPTQ | PTQ | 二阶信息 + 列分组 | 3~4 | <5% 损失 | LLaMA / Qwen |
| AWQ | PTQ | 激活感知, 保护 outlier | 3~4 | <3% 损失 | LLaMA / Qwen |
| SmoothQuant | PTQ | 激活难度 → 权重迁移 | 8 (W8A8) | <1% 损失 | 大模型通用 |
| BitNet | QAT | 1.58 bit 三元, STE | 1.58 | 85~90% | 2B / 7B |
| Bonsai | QAT + 蒸馏 | 1.125/1.71 bit + per-group FP16 + 蒸馏 + DSpark | 1.125 / 1.71 | 90~95% | 1.7B / 4B / 8B / 27B |
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 已有 FP16 LLM, 快速部署 | GPTQ / AWQ Q4_K_M | PTQ, 几小时搞定 |
| 服务器端推理, 精度优先 | Q8_0 / SmoothQuant W8A8 | 不掉点 |
| 笔记本推理, 平衡 | Q4_K_M / Q5_K_M | 速度/精度 sweet spot |
| 边缘设备 (树莓派等) | Q2_K / IQ2 系列 | 3~4 bpw, 内存紧 |
| 手机 | Bonsai 1-bit / BitNet | 唯一能塞下 27B 的方案 |
| 研究者训练小模型 | BitNet b1.58 | 从头训练, 论文友好 |
| 研究者训练大模型 | Bonsai | 蒸馏 + per-group, 大模型稳 |
| 数据/观点 | 来源 |
|---|---|
| Bonsai 1.71 / 1.125 bpw, 5.9 / 3.9 GB, 95% / 90% 精度 | prismml.com/news/bonsai-27b (2026-07-14) |
| 163 / 87 / 134 / 58 tok/s 速度数据 | prismml.com/news/bonsai-27b |
| "intelligence density 0.53 per GB" | prismml.com/news/bonsai-27b |
| "no higher-precision escape hatches" | prismml.com/news/bonsai-27b |
| Q1_0 group=128, Q2_0 编号 42, DSpark MODEL_ARCH | llama.cpp-prism/ggml/include/ggml.h:431-433 + conversion/dspark.py:37 |
| GGUF TYPE_COUNT 42 vs 43 (main vs prism) | llama.cpp-master/ggml/include/ggml.h:432 vs llama.cpp-prism/...:433 |
| "per-tensor scale 100× 精度差" | 业内共识: LLM 激活存在 outlier 通道 (SmoothQuant 论文 + Dettmers 2022 LLM.int8() 论文) |
| "LLM 权重 ~30~50% ≈ 0 (ternary 阈值化)" | Han et al. "Learning to Weight Pruning" 2022 + BitNet 论文 (Ma 2024) 实测 |
| "1-bit 矩阵乘比 FP16 快 5~10×" | BitNet 论文 benchmark + llama.cpp 实测 (CPU 推理) |
| "BitNet 1.58 bit, 85~90% 精度" | BitNet 论文 (Ma et al. 2024) |
| "GPTQ / AWQ / SmoothQuant PTQ 流派" | Frantar 2022, Lin 2023, Xiao 2023 论文 |
| "iPhone 17 Pro 6GB app 预算" | Apple iPhone 17 Pro 规格 |
| "RTX 5090 1.8 TB/s 带宽, 100 TFLOPS FP16" | NVIDIA RTX 5090 官方规格 (白皮书) |
| "M5 Max ~400 GB/s 带宽, ~10 TFLOPS FP16" | Apple M5 Max 官方规格 |
| "iPhone 17 Pro ~50 GB/s 内存带宽, ~2 TFLOPS" | Apple iPhone 17 Pro 官方规格 |
| "DSpark speedup = K/m (lossless)" | Li et al. EAGLE 论文 (2024) + Bonsai 公告实测 |
| "llama.cpp mmap 按需加载" | src/llama-mmap.cpp / src/llama-model-loader.cpp + ggml/src/ggml.c mmap 实现 |
| "vLLM 不 mmap, 全量读显存" | vllm/model_executor/weight_utils.py load_weights() |
| "PTQ 训练成本 5×" | Gholami et al. "Survey of Quantization Methods" 2022 + Bonsai 训练时实测 |
| "Q1_0 dot product = d * (2*Σ(bit*a) - Σ a)" | ggml/src/ggml-metal/ggml-metal.metal:3261 源码注释 |
| "0x020100FF = {-1, 0, 1, 2}" 查表 | ggml-cuda/vecdotq.cuh:763 注释 |
| "DSpark NBLOCKS=1024, BLK_THREADS=256" | common/dspark-markov.cu:13-16 |
| "Q2_0 block_q2_0 = 2B d + 64B packed = 66B/256 w" | llama.cpp-prism/ggml/src/ggml-quants.c:439 源码 |
| "CPU 端 Q2_0 共用 Q1_0/Q4_0 通用路径, 无专用 SIMD" | llama.cpp-prism/ggml/src/ggml-cpu/ops.cpp:667-680 |