/experiments/5_7_local_speed/

5.7 节实验:在你自己的机器上测小模型单流与并发速度,对比 4 bit 和 8 bit 两种量化(MLX、llama.cpp、vLLM)

目录文件列表

文件 / 目录说明大小
📁 ../返回上级目录-
📁 sample_output/子目录-
📄 README.md6.4 KB
📄 bench.py4.8 KB
📄 bench_mlx.py2.9 KB
📄 requirements.txt6 B

README.md

5.7 在你自己的机器上测速:单流、并发、两种量化

这份代码回答三个问题。一个小模型在你的机器上一路能跑多快;同时来好几路请求时,每一路慢多少、总共能出多少 token;同一个模型的两种量化(4-bit 和 8-bit)速度差多少。宣传里的 tok/s 往往只报其中一个数,这份代码把它们放在一张表里。

做法:起一个本地推理服务,同时发 1、4、8、16 路请求(显卡上再加 32 路)。每个请求约 800 token 输入,让模型从 1 往上数数,用 max_tokens 卡在 256 个输出 token,保证每一路的活一样多。记三个数:首 token 延迟、每一路的生成速度、所有路加起来每秒出多少 token。

有两个脚本:

模型都是 Qwen3.5-2B(后训练版,关闭思考模式)。

需要什么设备

安装与运行

cd 5_7_local_speed
uv venv && source .venv/bin/activate       # 没有 uv 就用 python3 -m venv .venv
uv pip install -r requirements.txt         # 只有 httpx

llama.cpp(Mac 或 NVIDIA)。 Mac 上 brew install llama.cpp;NVIDIA 显卡按 llama.cpp 仓库的说明编译 CUDA 版。-np 是服务能同时处理几路,要不小于你测的最大并发:

llama-server -hf unsloth/Qwen3.5-2B-GGUF:Q4_K_M --no-mmproj -ngl 99 -c 65536 -np 16 --port 8080
python bench.py --url http://127.0.0.1:8080 --label "llama.cpp Q4_K_M"
# 停掉服务,换成 Q8_0 再测一遍
llama-server -hf unsloth/Qwen3.5-2B-GGUF:Q8_0 --no-mmproj -ngl 99 -c 65536 -np 16 --port 8080
python bench.py --url http://127.0.0.1:8080 --label "llama.cpp Q8_0"

MLX(Mac)。

uv pip install mlx-lm
python bench_mlx.py --model mlx-community/Qwen3.5-2B-4bit --label "MLX 4bit"
python bench_mlx.py --model mlx-community/Qwen3.5-2B-8bit --label "MLX 8bit"

vLLM(NVIDIA,Linux)。 两种精度:原始的 bf16,和 vLLM 在加载时把权重转成的 FP8:

uv pip install vllm
vllm serve Qwen/Qwen3.5-2B --port 8000 --max-model-len 4096 --language-model-only
python bench.py --url http://127.0.0.1:8000 --label "vLLM bf16" --concurrency 1 4 8 16 32
vllm serve Qwen/Qwen3.5-2B --port 8000 --max-model-len 4096 --language-model-only --quantization fp8
python bench.py --url http://127.0.0.1:8000 --label "vLLM fp8" --concurrency 1 4 8 16 32

每次结果打印成一张表,同时存进 results/。

该看到什么

我们的样例输出在 sample_output/(2026-10-23)。Mac Studio(M3 Ultra)上的结果,每格是“每一路的生成速度 / 所有路加起来”,单位 token/秒:

并发路数 llama.cpp Q4_K_M llama.cpp Q8_0 MLX 4-bit MLX 8-bit
1 183 / 163 160 / 146 272 / 244 193 / 178
4 82 / 269 94 / 301 197 / 606 168 / 537
8 48 / 307 56 / 344 149 / 830 127 / 738
16 30 / 360 31 / 380 87 / 923 83 / 896

一张 RTX 5090(32 GB)上的结果,同样是“每一路 / 所有路加起来”:

并发路数 vLLM bf16 vLLM FP8 llama.cpp Q4_K_M llama.cpp Q8_0
1 323 / 309 390 / 368 490 / 401 414 / 348
4 264 / 951 372 / 1280 290 / 851 264 / 804
8 256 / 1771 369 / 2416 160 / 881 159 / 873
16 229 / 3112 319 / 4081 76 / 855 74 / 845
32 226 / 5647 254 / 6073 39 / 879 39 / 895

几个现象:

这些数字只说明“能跑多快”,不说明量化后质量掉了多少。量化对质量的影响,看公开的评测(正文 5.7 有引用),或者在你自己的任务上跑一遍对比。

换成你自己的数据

换模型:llama.cpp 换 -hf 后面的 GGUF 仓库和量化名,MLX 换 --model,vLLM 换 serve 后面的模型名。换负载:--max-tokens 改输出长度;想测长输入,改 bench.py 里的 FILLER(现在约 800 token)。--concurrency 改成你预计的同时在线人数。最该测的是你实际会用的组合:你会部署的那个推理栈、你会用的量化、你预计的并发。