构建一个媲美 Llama.cpp 的 Rust 推理引擎
摘要
Ferrox 是一个纯 Rust 推理引擎,可加载 GGUF 模型,并在 CPU、Metal 或 CUDA 上运行本地 LLM,提供 CLI 和兼容 OpenAI 的服务器。它的目标是在不使用任何绑定、从零编写的情况下,达到与 llama.cpp 相当的性能。
暂无内容
查看缓存全文
缓存时间: 2026/08/08 17:19
# Ferrox:构建一个媲美 llama.cpp 的 Rust 推理引擎
我花了最近几天时间构建了 Ferrox(https://github.com/antonellof/ferrox),这是一个纯 Rust 实现的推理引擎,用于在本地运行开源 LLM——支持稠密模型和专家混合(MoE)模型,可在 CPU、Apple Metal 或 CUDA 上运行。不绑定 llama.cpp 或 ggml,不包装任何现有运行时。每一个内核、每一个加载器、每一个调度决策都是从零手写的。
显而易见的问题是"为什么要在 llama.cpp 已经存在且表现出色的情况下再做一个?"诚实的回答是:我想在比"运行二进制文件"更深的层次上理解推理,而且我想要一个项目,让每一个性能声明都必须对照真实、知名的基准来赢得,而不是凭空断言。
## Ferrox 到底是什么
核心上,Ferrox 加载一个 GGUF(https://github.com/ggml-org/ggml/blob/master/docs/gguf.md)文件——与 llama.cpp 使用的量化模型格式相同——并在其上运行推理。有两种使用方式:
- **一个 CLI**,`ferrox`,带有与 llama.cpp 兼容的 flag。指向一个模型,获得补全结果。
- **一个服务器**,`ferrox-server`,支持 OpenAI 的 chat-completions API。任何基于 ChatGPT API 构建的东西——聊天 UI、Agent 框架、测试工具——都可以不加修改地直接使用它,只需指向 `localhost`。
在底层,模型权重直接从磁盘内存映射,永远不会完整解压到 RAM——反量化在点积中融合执行,恰好在这个权重真正被用到的那一刻。这与 llama.cpp 使用的技巧相同,也是两个引擎都能在只有几 GB 内存的笔记本电脑上运行 8B 参数模型(而非需要三十 GB)的重要原因。
## 试试看:构建、下载 GGUF、运行
Ferrox 不自带权重。你需要像使用 llama.cpp 一样下载本地的 `\.gguf` 文件——我推荐使用 Hugging Face CLI(https://huggingface.co/docs/huggingface_hub/guides/cli)(`pip install -U huggingface_hub`):
```
git clone https://github.com/antonellof/ferrox.git
cd ferrox
cargo build --release -p ferrox-cli -p ferrox-server --features metal
mkdir -p models
# 约 1.2 GB 的冒烟测试
hf download TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF \
tinyllama-1.1b-chat-v1.0.Q8_0.gguf --local-dir models
# 可选:小型 instruct 聊天模型(约 0.8 GB)
hf download bartowski/Llama-3.2-1B-Instruct-GGUF \
Llama-3.2-1B-Instruct-Q4_K_M.gguf --local-dir models
```
来自 README(https://github.com/antonellof/ferrox)的便捷起点:
| 模型 | Hugging Face 仓库 | 文件 |
|------|-------------------|------|
| TinyLlama 1.1B Chat Q8_0 | TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF(https://huggingface.co/TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF) | `tinyllama-1.1b-chat-v1.0.Q8_0.gguf` |
| Llama 3.2 1B Instruct Q4_K_M | bartowski/Llama-3.2-1B-Instruct-GGUF(https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF) | `Llama-3.2-1B-Instruct-Q4_K_M.gguf` |
| Llama 3.1 8B Instruct Q4_K_M | bartowski/Meta-Llama-3.1-8B-Instruct-GGUF(https://huggingface.co/bartowski/Meta-Llama-3.1-8B-Instruct-GGUF) | `Meta-Llama-3.1-8B-Instruct-Q4_K_M.gguf` |
| SmolLM2 135M Instruct Q8_0 | bartowski/SmolLM2-135M-Instruct-GGUF(https://huggingface.co/bartowski/SmolLM2-135M-Instruct-GGUF) | `SmolLM2-135M-Instruct-Q8_0.gguf` |
日常使用推荐 `Q4_K_M`,小型冒烟测试用 `Q8_0`。更多 GGUF:Hugging Face 上兼容 llama.cpp 的模型(https://huggingface.co/models?apps=llama.cpp&sort=trending)。Ferrox 目前验证过的模型见:`docs/MODELS.md`(https://github.com/antonellof/ferrox/blob/main/docs/MODELS.md)。
然后:
```
# 一次性补全
./target/release/ferrox -m models/tinyllama-1.1b-chat-v1.0.Q8_0.gguf \
-p "The capital of France is" -n 32 --temp 0 --no-cnv
# Metal 上聊天(GGUF 自带聊天模板时的默认行为)
./target/release/ferrox -m models/Llama-3.2-1B-Instruct-Q4_K_M.gguf \
-p "What is 2+2?" -n 64 --temp 0 -dev metal -ngl all
# OpenAI 兼容服务器
./target/release/ferrox-server \
-m models/tinyllama-1.1b-chat-v1.0.Q8_0.gguf \
--host 127.0.0.1 --port 8383 -dev metal -ngl all
curl -s -X POST http://127.0.0.1:8383/v1/chat/completions \
-H 'content-type: application/json' \
-d '{"model":"m","messages":[{"role":"user","content":"Hi"}],"max_tokens":32,"temperature":0}'
```
Flag 与 llama.cpp 保持一致(`-m`、`-p`、`-n`、`-t`、`--temp`、`-ngl`……)——完整参考见 `docs/CLI.md`(https://github.com/antonellof/ferrox/blob/main/docs/CLI.md)。一个值得了解的 Ferrox 特有参数:`--ctk q8_0`(或 `FERROX_CTK=q8_0`)在 Metal 上将 KV 缓存存储为 Q8_0 而非默认的 `f16`,这会以少量精度换取更长上下文下更小的 KV 内存占用。
## 为什么性能必须是可验证的,而不是宣称的
每个本地推理项目都声称自己很快。但几乎没有一个展示方法论。我不想给那堆噪音再添一笔,所以 Ferrox 的整个基准测试体系都是为了能让声明可以被证伪而构建的:
- 两台引擎背靠背运行:同一台机器、同一个 GGUF 文件、同一个后端。
- 预热运行、贪心解码、多次重复、报告中位数。
- 每个重要数字都绑定到仓库中的一个 JSON 凭据文件——重新生成它,数字要么经得起验证,要么经不起。
这种纪律在 Apple M2 Pro(主机 B)上带来了真实的结果。以下重点数字是 `benchmarks/RESULTS.md`(https://github.com/antonellof/ferrox/blob/main/benchmarks/RESULTS.md)中 fair-chat 测试套件的**预测 tok/s**——Gap 为 `llama / ferrox`(低于 1.0 表示 Ferrox 更快;接近 1.0 表示在约 5% 以内):
| 模型 | 后端 | Ferrox | llama.cpp | Gap |
|------|------|--------|-----------|-----|
| Llama-3.1-8B Q4_K_M | Metal | 28.3 tok/s | 27.6 tok/s | ~0.97×(持平 / Ferrox 略快) |
| Llama-3.2-1B Q4_K_M | Metal | 140.8 tok/s | 140.8 tok/s | 1.00×(持平) |
| TinyLlama-1.1B Q8_0 | Metal | 117.9 tok/s | 113.7 tok/s | ~0.96×(持平) |
| Qwen2.5-0.5B Q8_0 | Metal | 196.1 tok/s | 132.8 tok/s | ~0.68×(Ferrox 更快) |
| SmolLM2-135M Q8_0 | Metal | 290.2 tok/s | 241.2 tok/s | ~0.83×(Ferrox 更快) |
| Gemma-3-1B Q8_0 | Metal | 94.1 tok/s | 81.7 tok/s | ~0.87×(Ferrox 更快) |
| OLMoE-1B-7B Q4_0 | Metal | 88.4 tok/s | 156.8 tok/s | ~1.77×(llama 更快) |
| Mistral-7B Q4_K_M | Metal | 31.4 tok/s | 33.3 tok/s | ~1.06×(接近持平) |
| Phi-4-mini Q4_K_M | Metal | 50.0 tok/s | 53.1 tok/s | ~1.06×(接近持平) |
最关键的基准数字也是我最看重的那个:**Metal 上的 Llama-3.1-8B** 现在达到 **~0.97×**——在同一台主机和同一个 GGUF 文件上,Ferrox 比 llama.cpp 略快(预测 28.27 vs 27.55)。在 CLI 一次性补全路径上则是完全 **1.00×** 的平局(28.85 vs 28.64)。那一刻,这个引擎不再像是一个学术练习。
自最初基准以来的另一个重大进展:**Metal 上的 OLMoE**。早期记录大约落后 llama.cpp **~15 倍**(约 10 tok/s)。经过 Metal 专家放置优化后,现在达到了 **88.4 vs 156.8**(约 1.77 倍)——仍然落后,但已完全是另一个量级。Gemma-3 Metal 从落后翻转为领先。完整方法论和所有原始基准数据都在 RESULTS 文件中。
### 重新运行测试套件
如果你想自己重新生成这些基准数据(同一台主机、相同的 GGUF 文件、两个引擎):
```
python3 benchmarks/run_suite.py --skip-missing --fit-host
# 可选的 CLI 模式基准:
python3 benchmarks/run_suite.py --skip-missing --fit-host --mode cli
```
这会覆盖 `benchmarks/receipts/pins/` 下的基准数据,并通过 `render_results.py` 重新生成 `RESULTS.md`。没有编造的数字——如果某个基准数据缺失,表格会明确说明。`--fit-host` 会跳过不适合当前机器运行的模型(并在 darwin 上跳过 CUDA)。
## 性能提升来自哪里
两个架构选择贡献了大部分性能:
**将反量化融合到矩阵乘法中。**量化权重(4 位、8 位)永远不会被展开成完整精度缓冲区。反量化计算作为点积的一部分内联执行,因此你只在缓存中付出一次代价,而不是作为一次单独的、受内存带宽限制的遍历。
**特定于架构的 GPU 路径。**模型并非结构相同——Qwen 有逐头的 QK 归一化,Gemma-3 使用滑动窗口注意力和 GeGLU,Phi-3 融合了 QKV 和 FFN 投影。Ferrox 为每个模型实现了专门的 Metal 内核,而不是让所有模型都走一条通用注意力路径。这正是小型 Qwen 和 SmolLM2 模型在 Metal 上大幅超越 llama.cpp 的原因——内核匹配实际的算子形状,而不是为不需要的通用性付出开销。同样的思路也推动了 OLMoE Metal 的飞跃:专家放在 GPU 上,而不是一个披着 MoE 外衣的通用稠密路径。
## 实用性:为什么这不仅仅是基准测试练习
除了数字之外,Ferrox 确实是一种实用的本地运行模型方式:
1. **单个静态二进制。**无需 Python 环境,无需 CUDA 工具包版本碰运气,无需 `pip install` 依赖解析。复制二进制文件,指向一个 `\.gguf` 文件,运行即可。
2. **可直接替代现有工具。**OpenAI 兼容服务器意味着任何已经接入 ChatGPT API 的应用——LangChain 脚本、自定义聊天前端、评估框架——只需一行 base-URL 改动即可对接完全本地的模型。
3. **支持 MoE,而不仅仅是稠密模型。**OLMoE-1B-7B(https://github.com/antonellof/ferrox/blob/main/docs/MODELS.md)可在 CPU 和 Metal 上运行,且有经过验证的基准数据。Metal MoE 仍然落后于 llama.cpp,但差距正在缩小。这很重要,因为专家混合模型是前沿模型效率提升的重要来源——一个只处理稠密 Transformer 的推理引擎正变得越来越不完整。
4. **后端灵活,适配你真正拥有的硬件。**仅 CPU 的笔记本电脑、Apple Silicon、Nvidia GPU——一套代码覆盖全部三种场景,而不是三个独立的工具。当上下文长度开始吃统一内存时,量化 KV(Metal 上的 `--ctk q8_0`)会有所帮助。
## 尚未完成的工作
我宁可低估也不愿高估:
- **CUDA 性能工作暂停了。**测试套件支持 `--backend cuda`,但目前没有 CUDA 基准数据——需要一台有 GPU 的主机。
- **Metal 预填充**在更大模型上仍需对照 llama.cpp 观察——持平的数字主要来自解码阶段;`prompt_per_second` 还有更多提升空间。
- **Metal 上的 MoE**提升很大,但 OLMoE 仍然比 llama.cpp 慢约 1.8 倍。主机 B 上缺少 Qwen2-MoE / Mixtral 的基准数据(受限于 GGUF / RAM)。
- **Gemma-4-E2B**目前明确不支持(需要专门的引擎路径)——Ferrox 和 Homebrew 版 llama.cpp 都会拒绝它。
- **前沿规模的 MoE / MLA**——Kimi、GLM、DeepSeek——已有基础算子和合成栈,但还没有真正跑通过几百亿参数规模的完整 checkpoint。这既是软件问题,也是硬件问题。
## 结论
Ferrox 最初是为了真正理解推理内部机制,而不是把它当作 `pip install` 背后的黑盒。它变成了一个我真正会去使用的工具:一个加载 GGUF 文件并能在终端中聊天或提供 OpenAI 兼容 API 的单一二进制文件,速度在真实硬件上经得起参考实现的对比,而且有凭据数据作为证明。
如果你对量化推理的底层原理感到好奇,想要一种无依赖的本地运行开源模型方式,或者只是想挑基准测试方法论的毛病——这个仓库采用 Apache-2.0 许可,欢迎提交 issue 和 PR。
**更多资源:**
- Ferrox on GitHub(https://github.com/antonellof/ferrox)
- `benchmarks/RESULTS.md`(https://github.com/antonellof/ferrox/blob/main/benchmarks/RESULTS.md)——完整的基准测试数据集
- `docs/MODELS.md`(https://github.com/antonellof/ferrox/blob/main/docs/MODELS.md)——支持的架构和验证状态
- `docs/CLI.md`(https://github.com/antonellof/ferrox/blob/main/docs/CLI.md)——CLI flag 说明
- GGUF 格式规范(https://github.com/ggml-org/ggml/blob/master/docs/gguf.md)
- llama.cpp(https://github.com/ggml-org/llama.cpp)——本项目用来对标自身的参考实现
---
*写于 2026 年 8 月,关于 Ferrox 的构建经历,以及为什么性能声明应该附带凭据数据。*
相似文章
GPU上的无畏并发:在Rust中进行安全的GPU推理,与vLLM/SGLang竞争 [R]
cuTile Rust 引入了一种基于块(tile)的编程模型,利用 Rust 的所有权机制来保证 GPU 内核的内存安全和无数据竞争,基于该模型构建的 Grout 推理引擎在 Qwen3 模型上实现了与 vLLM/SGLang 相当的吞吐量。
我构建了一个GBNF语法编译器,使8B模型能够可靠地调用工具——以下是它的工作原理(深度解析)
一位开发者用Rust为llama.cpp构建了一个GBNF语法编译器,该编译器强制遵循工具调用的JSON模式,使小型模型(如8B参数)能够可靠地调用工具。该系统每次调用时将语法限制为仅匹配的工具,提高了可靠性。
@no_stp_on_snek: 我的第二个且迟交的 Build Small 参赛作品。10天,1位开发者:从头构建的 Rust 引擎 + 自定义 GPU 内核 vs vLLM 在 N…
一位开发者从头构建了一个 Rust 推理引擎,带有自定义 GPU 内核,在 Nemotron-30B 解码上优于 vLLM,达到 75.7 vs 57 tok/s,提交至 Build Small 黑客马拉松。
@QingQ77: 用纯 Rust 实现 LLM 推理引擎,针对每种硬件×模型×量化组合定制 CUDA 内核,跑出比 vLLM 和 TensorRT-LLM 更高的推理速度。 https://github.com/Avarok-Cybersecurity/a…
Atlas 是一个纯 Rust 实现的 LLM 推理引擎,通过为每种硬件×模型×量化组合定制 CUDA 内核,实现了比 vLLM 和 TensorRT-LLM 更快的推理速度。
@0xSero:关于 LLM 推理与部署,看这一篇就够了。你听说过:- vLLM - SGLang - llama.cpp - …
vLLM、SGLang、llama.cpp 与 ExLlamaV3 等主流开源推理引擎概览,助你轻松托管并运行大模型。