ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

两千行代码跑通 DeepSeek:纯 C++ CPU 推理引擎,性能接近主流实现

两千行代码跑通 DeepSeek:纯 C++ CPU 推理引擎,性能接近主流实现

一、项目核心释义

这是一套纯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=16

5.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/interactivecompletion
-i输入提示文本-
-n最大生成token数256
-t采样温度1.0
-ptop-p采样阈值0.95
-L锁定权重到内存,禁止换页关闭
-T滑动窗口上下文长度最大
困惑度计算模式
./build/main 模型权重目录/-mperplexity-f输入文本.txt
交互对话模式
./build/main 模型权重目录/-minteractive

5.5 性能调优建议

  • 线程数设置:推荐设置为物理核心数的一半,例如24核设12线程
    OMP_NUM_THREADS=12./build/main...
  • 内存锁定:有足够内存时加-L参数,强制权重留在内存,避免换页降速
  • 量化选择:追求精度选F8E5M2,追求速度选Q2_K,平衡选Q3_K
  • 避免交换:确保物理内存足够,权重换页到磁盘会导致速度暴跌

5.6 支持模型列表

模型Q2_KQ3_KF8E5M2FP16FP32
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推理实现范本,去掉了所有冗余的抽象与封装,让开发者能专注于架构本身,是学习研究、二次开发、轻量部署的优质技术参考。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进