项目简介
如何在没有 GPU 的笔记本上运行一个总参数量 284B 的大模型?
开源项目deepseek-v4-flash-0731-in-c给出了一条纯 CPU 路径:使用 C99 和 OpenMP 编写推理引擎,直接读取 DeepSeek-V4-Flash-0731 原生权重,不依赖 CUDA、PyTorch,也不要求把权重转换成其他模型格式。
GitHub:
https://github.com/shyringo/deepseek-v4-flash-0731-in-c
DeepSeek-V4-Flash-0731 是一个 284B-A13B MoE 模型:总参数量 284B,每个 token 激活约 13B。完整权重约 166.9 GB,但由于每个 token 只使用部分专家,引擎可以把冷专家留在磁盘上,仅把当前工作集放入内存。
环境要求
运行项目需要:
- 主流 64 位 CPU,例如 x86-64 或 ARM64
- 支持 POSIX
mmap、pread和 pthread 的系统环境 - 最低 8 GB CPU 内存
- 约 172 GB 可用磁盘空间
- C99 编译器、make、OpenMP、Git、curl 和 Bash
- Windows 用户使用 WSL2
8 GB 是可运行下限,不代表最佳性能。更多内存可以缓存更多 MoE 专家,但内存预算过高导致系统换页时反而会变慢。
编译并启动推理
以 Ubuntu 22.04 或 Windows WSL2 为例,先安装构建工具:
sudoapt-getupdatesudoapt-getinstall-ybuild-essentialgitcurl拉取并编译项目:
gitclone https://github.com/shyringo/deepseek-v4-flash-0731-in-c.gitcddeepseek-v4-flash-0731-in-cmake-j从 ModelScope 下载并校验固定版本的原生权重,下载支持断点续传:
scripts/download-dsv4.sh"$HOME/model/DeepSeek-V4-Flash-0731"启动常驻聊天:
scripts/try-dsv4.sh启动后直接输入问题即可。输入/reset开始新会话,按Ctrl-C可以停止当前回答但不卸载模型,输入/exit退出。
单次提问也可以直接写在命令行:
scripts/try-dsv4.sh"科技的边界在哪里?"实测性能:同时公开最佳场景和普通场景
参考环境如下:
| 项目 | 配置 |
|---|---|
| 操作系统 | Windows 11 + WSL2 Ubuntu 22.04.5 LTS |
| CPU | Intel Core i5-1340P,12 核 / 16 逻辑处理器 |
| 内存 | 安装 31.65 GiB,WSL2 可见 23.47 GiB |
| 磁盘 | Samsung MZVL41T0HBLB NVMe,权重位于 ext4 |
| 编译器 | GCC 11.4.0 + OpenMP |
| 推理配置 | 18 GiB 内存预算,12 线程,65536 上下文 |
完整有效测试记录:
| 场景 | 输入/输出 | TTFT | TPOT | 吞吐 | 专家读取 | 峰值 RSS |
|---|---|---|---|---|---|---|
| 普通回答,无可复用文本 | 5 / 64 token | 20.203 s | 1.705 s/token | 0.59 token/s | 70.67 GiB | 21.95 GiB |
| Prompt Lookup 完整命中 | 10 / 60 token | 26.748 s | 0.892 s/token | 1.12 token/s | 53.64 GiB | 22.19 GiB |
最佳 TPOT 0.892 s/token 来自有重复上下文可复用的 Prompt Lookup 场景。它不是普通问答的固定速度。该测试共完成 60 个输出 token,4 轮完整主模型验证接受 50/51 个 draft token,所有展示内容都经过主模型确认。
同条件关闭 Prompt Lookup 时,总耗时为 124.2 秒、TTFT 为 27.932 秒、TPOT 为 1.594 秒/token;开启后分别为 79.1 秒、22.875 秒和 0.939 秒/token,60 个输出 token 完全一致。
核心实现一:MoE 专家流式读取
普通的模型加载方式会尝试让大量权重常驻内存,而 166.9 GB 显然超出了多数笔记本的物理内存。
本项目把每层真正激活的 MoE 专家作为流式工作集:
- Router 确定当前 token 需要的专家。
- 命中缓存的专家立即进入计算。
- 未命中的专家从 NVMe 读取。
- 每读完一个专家就开始计算,不等待本层全部 I/O 完成。
- 每层使用独立的 LRU 缓存份额,减少跨层相互驱逐。
内存越多,能够保留的专家越多;内存较少时则增加磁盘读取,但仍可完成正确推理。
核心实现二:磁盘 I/O 与 CPU 计算流水线
专家权重采用双缓冲:一个缓冲区参与计算时,另一个缓冲区读取下一批专家。非专家层权重使用内存映射;计算第 L 层期间,内核 read-ahead 和后台读取线程加载第 L+1 层。
读取线程池在模型生命周期内复用,默认最多四个 worker,避免每层重复创建线程,也避免过多 I/O 线程与 OpenMP 计算线程争抢 CPU。
核心实现三:多 token 批处理
Prefill 和投机验证采用 layer-major 组织:加载一层权重后,连续处理 batch 中的所有 token,再进入下一层。同一份 FP8、FP4、BF16 权重读取和解码结果可以服务多个位置。
这类批处理对 MoE 尤其重要,因为多个 token 可能路由到相同专家。专家只需读取一次,就能处理所有相关 token。
核心实现四:低比特融合算子
FP4 和 FP8 权重不会预先展开成完整浮点矩阵。反量化直接在 GEMV 内核中完成:
- FP4 使用寄存器内查表
- FP8 快速路径使用 SIMD 位域转换
- Gate 和 Up 投影共享量化输入与一次 OpenMP 调度
- AVX2 路径并行处理八个输出行,同时保持每行原有浮点累加顺序
- MoE、Attention 和路由工作区在模型打开时预分配并循环使用
这样可以减少中间内存、重复量化和热路径内存分配。
核心实现五:常驻聊天与自动资源规划
交互模式会跨轮次保留模型映射、KV/compressor 状态、专家缓存和热权重。停止当前回答、开始下一轮聊天或执行/reset时,不需要重新加载 167 GB 权重。
启动脚本会根据物理内存、当前可用内存和 CPU 能力,自动决定上下文长度、热权重预算、专家缓存、OpenMP 线程和读取线程。需要手动控制时,只需设置一个总内存预算:
DSV4_MEMORY_GIB=18scripts/try-dsv4.sh推理正确性验证
不下载完整权重也可以运行测试:
maketest仓库内置四层 tiny 模型,连续执行 130 个位置,并与独立 Python 实现逐项比较,要求maxdiff=0.000000。完整模型另有固定 16-token oracle。GitHub Actions 同时使用 GCC 与 Clang 构建和测试。
总结
这个项目的目标不是把 284B 模型描述成“小模型”,而是让完整原生权重在笔记本的真实内存和磁盘条件下可运行、可聊天、可继续优化。
项目采用 Apache License 2.0,并基于 kimi-k3-in-c 的专家流式读取思路继续开发。复用代码、适配工作和原创优化在 NOTICE 与优化文档中有清晰边界。
GitHub 项目地址:
https://github.com/shyringo/deepseek-v4-flash-0731-in-c