一、项目核心释义
这是一套纯C++实现的DeepSeek全系列大语言模型CPU推理引擎,专为学习研究与轻量部署设计,整体代码不到2000行(不含格式化与JSON工具),远小于主流推理引擎的代码规模,结构清晰、易于修改与二次开发。
引擎完整支持DeepSeek-V2/Lite、V2.5、V3、V3.1(Terminus)、R1全系列模型,覆盖FP16、FP32全精度与Q2_K、Q3_K、F8E5M2等多档位量化方案,采用OpenMP并行调度,单批次解码性能接近主流推理引擎水平,是学习DeepSeek架构、研究CPU推理优化的极简工程范本。
核心特性:
- 极简代码:核心推理代码不到2000行,结构扁平,无复杂抽象,易于阅读与修改
- 全系列覆盖:支持DeepSeek V2到V3.1及R1全系列模型
- 多档位量化:从FP32全精度到2bit极致压缩,覆盖8种量化档位
- 纯CPU推理:完全基于CPU指令集优化,支持AVX2与F16C指令集加速
- 多运行模式:支持文本补全、困惑度计算、口令测试、交互对话四种模式
二、行业核心技术知识点
2.1 纯C++推理引擎的学习价值
主流推理引擎代码量庞大、抽象层数多,学习者很难看清推理本质。极简纯C++实现的核心价值在于:
- 没有框架黑盒,从权重加载到token生成每一步都清晰可见
- 代码量小,可快速通读全量代码,完整理解推理链路
- 适合作为测试床,快速验证新的算子、量化方法、调度策略
- 易于裁剪与移植,可快速嵌入到原生应用、嵌入式系统
2.2 DeepSeek模型的架构特点
DeepSeek系列采用多 latent 注意力(MLA)架构,和传统多头注意力不同:
- 用更短的 latent 维度做注意力计算,再投影回完整维度
- 配合门控机制,在保证效果的同时大幅减少计算量
- 是推理性能优化的重点,也是区别于LLaMA架构的核心部分
- 对内存带宽敏感,计算密度相对较低,CPU推理优化重点在访存优化
2.3 K量化技术:平衡精度与体积
K量化是当前CPU推理最主流的量化方案,采用块-超块两级分层结构存储权重:
- 按块量化权重,每块有独立的缩放因子与零点
- 多个小块组成一个超块,共享部分参数,降低存储开销
- Q2_K、Q3_K分别对应2bit、3bit平均位宽,内存占用分别为全精度的1/8和1/5左右
- 相比简单截断量化,K量化精度损失小很多,是CPU大模型推理的标准方案
2.4 F8E5M2分块量化
F8E5M2是针对DeepSeek架构优化的分块量化方案:
- 权重按128×128块量化,MoE门控与层归一化保留全精度
- 相比逐张量量化,块级量化的精度明显更好
- 避免了简单截断量化在DeepSeek模型上出现的输出混乱问题
- 平衡了精度与推理速度,是CPU推理的性价比之选
2.5 CPU推理的性能瓶颈
大模型CPU推理的核心瓶颈从来不是计算量,而是内存带宽:
- 解码阶段每个token都要读取所有权重,权重读取时间占比超过70%
- 多核扩展容易遇到内存墙,线程太多反而因为争抢带宽导致性能下降
- 最优线程数通常是物理核心数的一半,而不是开满所有线程
- 量化降低权重体积,本质上就是降低内存带宽压力
三、整体架构设计思路
3.1 三层极简架构
引擎采用三层扁平架构,没有复杂的抽象与封装,每层职责清晰。
┌──────────────────────────────────────────────────────┐ │ 交互与工具层 │ │ 命令行解析 / 四种运行模式 / 权重转换工具 │ └──────────────────┬───────────────────────────┘ │ ┌──────────────────────────────────────────────────────┐ │ 推理核心层 │ │ 词表 / 采样器 / Transformer层 / 注意力 / MLP │ └──────────────────┬───────────────────────────┘ │ ┌──────────────────────────────────────────────────────┐ │ 基础运算层 │ │ 张量运算 / 量化反量化 / OpenMP并行 / 向量指令 │ └──────────────────────────────────────────────────────┘- 基础运算层:最底层,提供张量运算、量化反量化、向量指令优化、OpenMP并行
- 推理核心层:模型核心计算部分,包括词表嵌入、Transformer层、多潜注意力、MLP、采样器
- 交互与工具层:命令行参数解析、四种运行模式切换、配套权重转换工具
3.2 完整推理流程
加载权重与配置 → 输入提示分词 → 嵌入层 → 逐层Transformer计算 ↓ 循环生成 ← 采样下一个token ← 输出层计算 ← 最后层归一化3.3 核心设计原则
- 极简优先:能简化的抽象全部简化,没有多余封装,代码最短
- 性能对齐:核心算子复用成熟的向量点积内核,性能接近主流引擎
- 纯CPU实现:完全不依赖GPU,只通过CPU指令集优化
- 易于修改:结构扁平,变量命名清晰,方便作为测试床修改实验
四、核心代码实现原理
4.1 权重加载与量化存储
引擎支持原生safetensors转换后的自定义权重格式,不同量化方案对应不同的存储结构。
权重头部结构
structModelHeader{intdim;// 隐藏维度intn_layers;// 层数intn_heads;// 注意力头数intn_latent;// latent注意力维度intvocab_size;// 词表大小intmax_seq_len;// 最大序列长度QuantType quant_type;// 量化类型};量化权重加载
boolload_weights(conststd::string&path,Model&model){std::ifstreamfile(path,std::ios::binary);// 读取头部信息ModelHeader header;file.read((char*)&header,sizeof(header));// 按量化类型分配对应结构switch(header.quant_type){caseQUANT_Q2_K:load_q2_k_layers(file,model,header);break;caseQUANT_Q3_K:load_q3_k_layers(file,model,header);break;caseQUANT_F8E5M2:load_f8e5m2_layers(file,model,header);break;caseQUANT_FP16:load_fp16_layers(file,model,header);break;}returntrue;}代码讲解:
- 权重文件头部存储模型配置与量化类型,加载时先读头再按对应格式加载
- 不同量化方案有独立的加载与计算代码,不用运行时判断分支
- 加载时直接按层存储,推理时按层顺序调用,内存布局连续
4.2 Transformer层前向传播
DeepSeek采用Pre-LN结构,先归一化再做注意力,残差连接。
voidtransformer_forward(Layer&layer,Tensor&x,intseq_len){// 注意力分支:先RMSNorm,再注意力,再加回残差Tensor residual=x;rms_norm(x,layer.attn_norm);Tensor attn_out=multi_latent_attention(x,layer.attn,seq_len);x=tensor_add(residual,attn_out);// MLP分支:先RMSNorm,再前馈网络,再加回残差residual=x;rms_norm(x,layer.mlp_norm);Tensor mlp_out=mlp_forward(x,layer.mlp);x=tensor_add(residual,mlp_out);}代码讲解:标准的Pre-LN Transformer结构,和模型训练时完全一致,保证输出对齐。注意力采用DeepSeek特有的多潜注意力架构,是和通用LLaMA类模型最大的区别。
4.3 多潜注意力(MLA)实现
多潜注意力是DeepSeek的核心架构,用低维度latent做注意力计算,再通过门控投影回完整维度。
Tensormulti_latent_attention(Tensor&x,AttnWeights&w,intseq_len){intdim=x.shape[1];intn_heads=w.n_heads;intlatent_dim=w.latent_dim;// 1. 投影到latent维度:Q、K、VTensor q_latent=linear(x,w.q_proj);// [seq, latent]Tensor k_latent=linear(x,w.k_proj);Tensor v_latent=linear(x,w.v_proj);// 2. 多头拆分q_latent=reshape(q_latent,{seq_len,n_heads,latent_dim/n_heads});k_latent=reshape(k_latent,{seq_len,n_heads,latent_dim/n_heads});v_latent=reshape(v_latent,{seq_len,n_heads,latent_dim/n_heads});// 3. 注意力计算:QK点积 + Softmax + 乘VTensor scores=matmul_transpose(q_latent,k_latent);scores=softmax_last_dim(scores);Tensor attn_out_latent=matmul(scores,v_latent);// 4. 合并多头attn_out_latent=reshape(attn_out_latent,{seq_len,latent_dim});// 5. 门控投影回完整维度Tensor gate=silu(linear(x,w.gate_proj));Tensor output=linear(attn_out_latent,w.out_proj);output=tensor_mul(output,gate);returnoutput;}代码讲解:
- 先把输入投影到更低的latent维度做注意力计算,减少计算量
- 标准的多头注意力计算,在latent空间完成
- 最后通过门控机制投影回完整隐藏维度,兼顾效果与效率
- 这是DeepSeek架构的核心,也是和普通LLaMA推理实现最大的不同
4.4 采样器实现
支持贪心采样与top-p采样,通过温度控制随机性。
intsample_token(Tensor&logits,floattemperature,floattop_p){// 温度缩放if(temperature!=1.0f){for(float&v:logits.data){v/=temperature;}}// Top-P采样:从大到小累加,直到超过p,截断后面的if(top_p<1.0f){top_p_filter(logits,top_p);}// Softmax转概率Tensor probs=softmax(logits);// 按概率采样returncategorical_sample(probs);}代码讲解:先做温度缩放,再做top-p截断,最后转成概率分布采样。温度越高随机性越强,top-p越小越集中在高概率token上。
4.5 OpenMP并行与性能调优
采用OpenMP做循环级并行,不用复杂的线程池与屏障,实现简单,开销小。
// 矩阵乘向量化+OpenMP并行voidmatmul_vec(constTensor&a,constTensor&b,Tensor&c){intM=a.shape[0];intK=a.shape[1];intN=b.shape[1];#pragmaomp parallelforfor(inti=0;i<M;i++){for(intj=0;j<N;j++){floatsum=0.0f;// 内层向量化点积sum=vec_dot(a.data+i*K,b.data+j*K,K);c.data[i*N+j]=sum;}}}代码讲解:
- 外层循环用OpenMP并行,每个线程计算一行输出
- 内层用向量指令优化点积,充分利用CPU SIMD能力
- 不使用全局线程池,避免线程切换与同步开销
- 最佳线程数通常为物理核心的一半,避免内存带宽争抢
五、环境配置与运行全教程
If you need the complete source code, please add the WeChat number (c17865354792)
5.1 环境要求
- 编译器:支持C++20标准的编译器(GCC 10+、Clang 12+、MSVC 2022+均可)
- 指令集:FP16等量化需要AVX2与F16C指令集支持
- 内存:根据模型大小而定,V2-Lite FP16约8GB,V3 Q2_K约206GB
- 系统:Linux、Windows、Mac均可
- Python:3.8+,仅用于权重转换,推理过程不需要
5.2 编译构建
gitclone<项目地址>cd推理引擎make编译完成后可执行文件生成在build/目录下。
也可以直接用Makefile指定优化选项:
makeOMP_NUM_THREADS=165.3 模型准备
需要先把原生safetensors格式的模型转换为引擎支持的量化格式。
安装转换依赖
pipinstall.转换示例
# FP16精度转换python convert.py--quantfp16 输出目录 原生模型目录/# Q2_K量化转换python convert.py--quantq2_k 输出目录 原生模型目录/# F8E5M2分块量化转换python convert.py--quantf8e5m2 输出目录 原生模型目录/转换完成后会生成.dseek后缀的权重文件。
5.4 运行推理
基础文本补全
./build/main 模型权重目录/-i"什么是大语言模型?"指定参数运行
./build/main 模型权重目录/\-i"解释一下深度学习"\-mcompletion\-n256\-t0.8\-p0.9常用参数说明:
| 参数 | 说明 | 默认值 |
|---|---|---|
-m | 运行模式:completion/passkey/perplexity/interactive | completion |
-i | 输入提示文本 | - |
-n | 最大生成token数 | 256 |
-t | 采样温度 | 1.0 |
-p | top-p采样阈值 | 0.95 |
-L | 锁定权重到内存,禁止换页 | 关闭 |
-T | 滑动窗口上下文长度 | 最大 |
困惑度计算模式
./build/main 模型权重目录/-mperplexity-f输入文本.txt交互对话模式
./build/main 模型权重目录/-minteractive5.5 性能调优建议
- 线程数设置:推荐设置为物理核心数的一半,例如24核设12线程
OMP_NUM_THREADS=12./build/main... - 内存锁定:有足够内存时加
-L参数,强制权重留在内存,避免换页降速 - 量化选择:追求精度选F8E5M2,追求速度选Q2_K,平衡选Q3_K
- 避免交换:确保物理内存足够,权重换页到磁盘会导致速度暴跌
5.6 支持模型列表
| 模型 | Q2_K | Q3_K | F8E5M2 | FP16 | FP32 |
|---|---|---|---|---|---|
| DeepSeek-V2-Lite | ✅ | ✅ | ✅ | ✅ | ✅ |
| DeepSeek-V2 | ✅ | ✅ | ✅ | ✅ | ✅ |
| DeepSeek-V2.5 | ✅ | ✅ | ✅ | ✅ | ✅ |
| DeepSeek-V3 | ✅ | ✅ | ✅ | - | - |
| DeepSeek-V3.1 | ✅ | ✅ | ✅ | - | - |
| DeepSeek-R1 | ✅ | ✅ | ✅ | - | - |
六、落地用途与场景
6.1 深度学习教学与研究
非常适合高校、研究机构做深度学习推理教学,代码量小、结构清晰,没有框架黑盒,学习者能完整理解从权重到token生成的每一步,也方便作为测试床验证新的量化方法、算子优化。
6.2 轻量CPU部署
不需要GPU的轻量部署场景,比如内部工具、离线文本处理、数据标注辅助等,纯CPU就能运行,不需要额外硬件投入,部署简单,资源占用低。
6.3 模型性能测试床
研究DeepSeek架构、测试量化方案、对比推理性能的场景,极简引擎修改方便,变量可控,适合做对照实验,快速验证技术方案。
6.4 原生应用嵌入
需要嵌入大模型能力的原生桌面应用、离线工具,纯C++实现可以直接静态链接,不需要Python运行时,不依赖第三方框架,集成简单,发布体积小。
6.5 服务器批量推理
CPU服务器批量做文本处理、困惑度计算、数据评估等场景,纯CPU推理成本低,可充分利用CPU服务器的核心与内存资源,不用配置GPU。
七、总结
这套纯C++实现的DeepSeek系列推理引擎,用不到2000行的极简代码,完整实现了从权重加载到采样生成的全链路推理,支持全系列模型与多档位量化,性能接近主流推理引擎。
它最大的价值不是生产级的极致性能,而是提供了一套简洁、完整、可修改的DeepSeek推理实现范本,去掉了所有冗余的抽象与封装,让开发者能专注于架构本身,是学习研究、二次开发、轻量部署的优质技术参考。