五层地图, 自底向上:

  • L0 物理量 —— 容量和带宽, 决定能不能跑、跑多快
  • L1 内存拓扑 —— 独立显卡 / Mac 统一内存 / DGX Spark, 我的机器能装多大
  • L2 模型 —— 参数量、量化、格式, 怎么把模型压进去
  • L3 运行时 —— llama.cpp / vLLM / SGLang / MLX, 谁来跑

L0: 两个物理量

物理量决定什么备注
容量模型能不能跑硬门槛
带宽每个 token 有多快decode 阶段几乎完全由它决定

两个阶段: prefill 和 decode

一次对话被切成两段, 瓶颈完全不同:

阶段干什么瓶颈用户感知
prefill一次性读完你的 prompt, 算出 KV cache算力 (FLOPS)TTFT, 首字延迟
decode一个一个吐 token带宽tok/s, 吐字速度

prefill 是并行的, 几千个 token 一起算; decode 是串行的, 每个 token 都要把权重完整读一遍.

所以下面的公式只管 decode. 长 prompt 卡在第一个字上, 是 prefill 的锅 见 L3 的 SGLang.

tok/s 带宽计算

生成下一个 token, 必须把这一步用到的全部权重从内存读进计算单元. 读完算一下, 再读下一轮.

理论 tok/s = 内存带宽 / ( 激活参数量 * 每参数字节数 )

例如: 7B, Q4 (约 0.5 byte/参数) 每 token 约读 3.5 GB

型号带宽理论 tok/s
RTX40901000 GB/s280 tok/s
DGX Spark273 GB/s78 tok/s
PC ddr580 GB/s23 tok/s

实际上会再打折 (kernel 效率、KV cache、采样), 但数据级对

KV cache: 容量的第二个吃货

权重是固定开销, KV cache 是变动开销 —— 它随上下文长度线性增长.

每读进一个 token, 都要把它在每一层的 K 和 V 存下来; 后面每生成一个新 token 都要回看这些缓存. 不存就得重算, 复杂度 O(n²).

KV cache ≈ 2 × 层数 × KV 头数 × head_dim × 上下文长度 × 每元素字节数

拿 Llama3-8B (32 层, 8 个 KV 头, head_dim 128, FP16) 算, 每 token 约 128 KB:

上下文KV cache
4K0.5 GB
32K4.3 GB
128K17 GB

对比一下: 这个模型 Q4 的权重才 4.9 GB. 32K 上下文的 KV cache 已经和权重一样大, 128K 是权重的三倍多.

这就是「模型明明装得下, 聊着聊着却 OOM」的原因. 三个办法:

  • 调小上下文 (num_ctx / --ctx-size), 最直接
  • 量化 KV cache 到 Q8, 减半
  • 换 KV 头数更少的模型 (GQA / MLA, 现在的新模型基本都做了)

L1: 内存拓扑

离散 GPU

最常见的 NVIDIA 台式机, 物理上是两块内存, 中间一座窄桥

特点说明
显存 (VRAM)容量小、带宽极大权重必须在这里才快
内存 (RAM)容量大、带宽小一个数量级权重掉到这里 = 灾难性 offload

Mac 统一内存

CPU 和 GPU 共享同一块物理 DRAM, 没有 PCIe 拷贝, 也没有显存满了掉到内存这种断崖下降

芯片容量上限带宽
M4 Pro最高 64GB~273 GB/s
M4 Max最高 128GB410–546 GB/s
M3 Ultra最高 512GB819 GB/s

特点:

  • 容量: 轻松装 70B Q4 大模型, Ultra 能装更大
  • 带宽: 比 5090 ~1792 GB/s 慢, 但比「GPU 装不下、PCIe offload」快一个数量级
  • 软件: 没有 CUDA, 主力是 MLX 和 llama.cpp 的 Metal 后端
  • 适合部署一张消费卡装不下的模型, 接受 tok/s 不是巅峰

DGX Spark

NVIDIA 版统一内存小主机, 定位桌面私人超算

  • 内存形态: 128GB 统一 LPDDR5
  • 带宽: 273 GB/s
  • 功耗: ~240W 整机

我的显存能跑多大

经验公式:

显存需求 ≈ 参数量(B) × 每参数字节数 × 1.2

那个 1.2 是 KV cache 和框架开销的余量, 按 4K 上下文估, 上下文拉长要再往上加. Q4_K_M 的每参数字节数实测约 0.55 (L0 为了好算取了 0.5).

常见档位:

显存 / 内存舒服跑挤一挤
8 GB7B / 8B Q414B Q4_K_S + 短上下文
12 GB14B Q414B Q4 + 32K 上下文
16 GB14B Q432B Q3 (质量掉一档)
24 GB (3090 / 4090)32B Q432B Q4 + 32K 上下文
48 GB (双卡 / A6000)70B Q470B Q4 + 长上下文
64 GB 统一内存70B Q4100B 级 MoE
128 GB 统一内存120B 级235B MoE (Q3)

超出显存不是跑不了, 是掉进 offload —— 权重挤到内存, 带宽从 1000 GB/s 掉到 80 GB/s, tok/s 断崖. 宁可降一档参数量, 也别让它 offload.

总结:

  • 小模型要吞吐 独立 GPU (带宽)
  • 大模型要能跑 统一内存 (容量)

L2: 模型

三个物理量:

物理量说明
总参数容量 (以及磁盘的体积)
激活参数每个 token 的计算量 + 有效搬运量 速度
量化每参数字节数 同时缩小容量和带宽

激活参数

  • Dense (稠密)
    • 每个 token 过全部参数. 总参数 = 激活参数
  • Sparse (稀疏)
    • 理念: 不是所有参数都对每一个 token 有用, 不同 token 只需要模型一部分”知识”. 因此可以只激活子集, 其余参数闲置
    • MoE 是目前大模型里最成功、规模最大的稀疏实现

MoE 怎么改写公式

拿 Qwen3-235B-A22B 举例, 名字里两个数字正好对应两个物理量:

数字是什么决定算出来
235B总参数容量Q4 约 130 GB, 全部要装进内存
A22B激活参数 (Active)速度每 token 只搬 22B, 约 12 GB

于是出现一个反直觉的组合: 容量像 235B, 速度却像 22B.

  • M3 Ultra (819 GB/s, 512GB): 装得下, 理论 819 / 12 ≈ 68 tok/s
  • 一张 4090 (1000 GB/s, 24GB): 装不下, 大部分权重掉进内存 offload, 结果比 Mac 慢一个数量级

模型名怎么读

Qwen3-235B-A22B-Instruct-2507-Q4_K_M

含义
Qwen3家族和代际
235B总参数 要多少内存
A22B激活参数 多快. 只有 MoE 才有这段
Instruct指令微调版, 能聊天. 对应的 Base 是基座, 只会续写
2507版本日期 (2025 年 7 月)
Q4_K_M量化格式 见下一节

Dense 模型的名字短一截: Qwen3-32B-Instruct-Q4_K_M, 没有 A 那段.

量化

用精度换容量和带宽

量化 = 用更少的比特去存 (有时也去算) 权重 / 激活, 换体积、显存和速度. 常见做法为:

  • 整数量化: INT8 / INT4 / GGUF 的 Q4_K_M
  • 低精度浮点: FP16 / BF16 / FP8 / NVFP4
格式典型场景
GGUF Q4_K_M / Q5 / Q8llama.cpp / Ollama 默认生态, CPU+GPU 都能跑
AWQ / GPTQ / EXL2主要为 GPU 推理 (vLLM、exllamav2)
FP8 / NVFP4新卡原生 (Blackwell). Spark 的 1 PFLOP 卖点就在 FP4
MLX 量化Mac 专用

代价不是零. 大致的质量衰减:

量化每参数字节质量
FP16 / BF162原精度, 基准
Q8~1几乎无损, 测不出差别
Q6_K~0.8极接近 Q8
Q4_K_M~0.55有退化但日常用不出来 甜点
Q3_K_M~0.42开始能感觉到, 长逻辑链容易崩
Q2~0.3明显劣化, 不建议

一条经验: 同样显存下, 大模型的低量化通常优于小模型的高量化 —— 32B Q3 一般比 14B Q8 强, 但别掉到 Q3 以下.

总结:

  • Q4_K_M: 是质量和体积的甜点; Q8: 接近原精度
  • 量化让公式里的「每参数字节数」变小, 同样带宽下 tok/s 近似翻倍 (Q8 Q4)

格式

下载到的模型, 本质是三样东西:

  • 一堆张量 (weights): 几十亿个数字, 通常是 float16 / bfloat16
  • 一张计算图 (architecture): 矩阵乘、RMSNorm、Attention、RoPE…固定的算子序列
  • 附属元数据: tokenizer、chat template、词表、超参

所谓格式, 就是: 这些数字和元数据怎么打包到磁盘上, 好让某个运行时能又快、又省、又安全地读出来, 并在特定硬件上算

格式不是模型质量本身. 同一个 qwen3.8-27B, 可以是 safetensors、GGUF、ONNX、TensorRT engine, 数学上是同一套权重, 只是包装和数值精度不同

下面六种, 按「你什么时候会撞上」排优先级:

格式什么时候遇到优先级
GGUF用 Ollama / llama.cpp, 本地部署最常见⭐ 主线
safetensors从 HuggingFace 下载原始权重⭐ 主线
MLX 权重Mac 上跑⭐ Mac 主线
.pt / .pth老项目、训练 checkpoint背景
ONNX浏览器、手机 / Windows NPU背景
TensorRT engine生产集群背景

标「背景」的三种, 知道名字和它解决什么问题就够了.

.pt/.pth

  • PyTorch 原生, 本质 Python pickle 把任意 Python 对象序列化
  • 致命问题:
    • pickle 能执行任意代码 (加载恶意模型 = RCE)
    • 绑死 Python, C++/Rust/浏览器 读不了
    • 必须整文件反序列化, 不能 mmap

Safetensors

  • hugging face 为干掉 pickle 设计的, 开源权重的首选发布格式
  • 特点:
    • 结构极简
    • 安全: 只有数字, 没有可执行代码
    • 快: mmap() 零拷贝, 可按层懒加载
    • 语言无关: C++/Rust/Python 都能读
    • 不自包含计算图: 只存权重
  • 用途: 训练、微调、LoRA、vLLM/SGLang 生产服务的输入

GGUF

  • llama.cpp 团队针对 GGML 的改进版, 单文件开箱即用
  • 一个 .gguf 文件就能跑, 里边塞进了
    • 全部权重 (已经量化好)
    • 架构元数据、超参
    • tokenizer、chat template
  • 缺点:
    • 从 safetensors 转换而来, 不是训练格式
    • 微调支持很弱 (要微调回去用 safetensors)
    • 不是所有新架构都第一时间被 llama.cpp 支持

ONNX

  • ONNX (Open Neural Network Exchange) 不只存权重, 还把 整张算子图 存进去
  • 特点:
    • 框架无关: PyTorch 导出 → ONNX Runtime / OpenVINO / TensorRT / CoreML / WebGPU 都能吃
    • 图优化: 算子融合、常量折叠、在编译期做
    • 覆盖 LLM 之外 的一切: CV、ASR、embedding、分类器
  • 常用于: 浏览器 (transformers.js / WebLLM)、Windows NPU、手机 NPU 等

TensorRT Engine

  • 为某一块 NVIDIA GPU 编译出来的机器码, 生产环境用
  • 流程: safetensors → TensorRT-LLM 编译 (针对 H100 / B200、特定 batch、特定最大序列长度) → .engine
  • 优点: 同卡上通常比 vLLM 再高 15~30% 吞吐, 适合「模型稳定、卡固定、请求海量」的生产
  • 代价:
    • 换卡、改 batch、改最大长度 = 重新编译 (H100 上几十分钟)
    • NVIDIA 绑定

MLX 权重

  • 围绕 Apple 统一内存设计, 权重以量化过的 safetensors 发布
  • MLX 通常比 llama.cpp 的 Metal 后端快 15~40%
  • 只能跑在 Apple Silicon 上

L3: 运行时

硬件内核

CUDA (NVIDIA)

  • 生态最厚: 几乎所有生产引擎的最快路径就是 CUDA
  • 代价: 锁 NVIDIA; 多卡要 NCCL、NVLink

Metal (Apple)

  • Apple Silicon 的 GPU API, 对应 CUDA 在 Mac 上的位置
  • 统一内存: CPU/GPU 共享同一块物理内存, 没有 PCIe 拷贝
  • 限制: 单机、无多机集群

Vulkan (跨厂商 GPU)

  • llama.cpp 用它做「一块二进制跑很多卡」的便携路径
  • 性能通常弱于厂商原生 (CUDA / HIP / Metal), 但可移植性最好
  • 适合: 消费级杂牌 GPU、不想为每家写一份 kernel

NPU (Neural Processing Unit)

  • 不是一种 API, 是一类芯片
  • 为 低功耗、固定图、量化模型 设计 (INT8/INT4)

推理引擎

llama.cpp

  • 便携性之王, 入门首选
  • 语言: C/C++, 几乎零依赖, 一个二进制就能跑
  • 权重格式: GGUF
  • 后端: CPU SIMD、CUDA、Metal、Vulkan、甚至一些 NPU 实验路径
  • 调度: 偏单机、单用户; 有 server, 但不是为千并发设计
  • 代表产品: Ollama、LM Studio 底层大多是它

强项: CPU、笔记本、边缘、任何奇怪硬件
弱项: 高并发吞吐吃不满数据中心 GPU; 量化格式和 HuggingFace 主线不完全互通

vLLM

  • 生产环境首选
  • 硬件: NVIDIA 一等公民, AMD ROCm / 部分 TPU 也在跟
  • 模型: 覆盖最广, 换模型几乎不用重编译
  • 强项: 文档、生态、稳定性、「它大概能跑」

SGLang

  • 共享前缀和 Agent 场景首选
  • 把 KV 放进一棵前缀树 (radix tree), system prompt、同一篇 RAG 文档、多轮对话历史, 第二次遇到直接命中, 不再 prefill
  • 强项:
    • 约束解码 / 结构化输出 (JSON schema)
    • 多步 Agent、工具调用
    • Prefill/Decode 分离部署
  • 适合: 聊天、RAG、Agent
  • 不适合: 每条 prompt 都独一无二的批量生成

TensorRT-LLM (TRT-LLM)

  • 先把模型编译成一份 TensorRT engine
  • 强项: 固定模型、长期在线、要压榨最后 10–20% 的吞吐和 TTFT
  • 弱项: NVIDIA only, 换模型/改 shape 要重编译, 运维重

MLX (引擎)

  • Apple Silicon 原生, 后端几乎就是 Metal
  • 优点:
    • 量化 Q4/Q8 成熟, 和 HuggingFace 权重转换顺
    • MLX 往往快于 llama.cpp Metal, 因为少一层抽象、吃满统一内存
  • 缺点: Apple Only, 没有数据中心调度栈

ORT

  • 微软的跨平台运行时, 到处能跑的工业胶水, 不是跑得最快的 serving 引擎
  • ONNX Runtime / ONNX Runtime GenAI
  • 灵魂是 Execution Provider (EP): 同一份 ONNX 图, 可挂到不同后端
  • 强项: Windows Copilot、Office、端侧、NPU
  • 弱项:
    • 不是吞吐冠军
    • 动态图、最新架构 (MLA、新 MoE) 往往慢半拍
    • 转换/量化链路比 GGUF 烦
最佳场景硬件核心技术上手量化
llama.cpp本地、CPU、边缘CUDA / Metal / Vulkan / CPUGGML + GGUF极易GGUF 全家桶
vLLM通用生产 servingNVIDIA (主)、AMDPagedAttention + 连续批处理FP8、AWQ、GPTQ、GGUF…
SGLang多轮 / RAG / AgentNVIDIA 为主RadixAttention与 vLLM 类似
TRT-LLM固定模型极致吞吐仅 NVIDIA编译期图优化 + Tensor CoreFP8、NVFP4、INT8
MLXMac 本地 / 研究仅 Metal统一内存 + 懒计算Q4/Q8
ORT端侧、NPU、跨平台产品CUDA / CoreML / QNN / OpenVINOEP 插件ONNX 量化工具链