基于FPGA的MoE模型推理实现:从架构拆解到工程实践 📅 发布时间:2026/9/8 15:28:49 👁 浏览次数: 1. MoE模型到底是什么——用“多个小专家”拼出大模型MoE全称是Mixture of Experts混合专家模型。这两年它在大模型圈子里火得厉害很多人第一次听到这个名字是在GPT-4、Mixtral这些模型的架构说明里——只要一提到“稀疏激活”“专家路由”十有八九就是MoE路线。但说实话MoE并不是什么新概念早在1991年就有学者提出了这个思路只是当时算力和数据都不支持把它推到太大的规模。现在借着大模型的东风它才真正从论文变成了主流工程方案。它的核心思想其实非常好理解一个庞大的模型不一定非得让所有参数对每个输入都起到作用而是可以把模型拆成若干个“专家”子网络每个专家擅长处理不同类型的数据分布然后由一个“路由”模块也叫门控模块、Router、Gate来决定某个输入应该交给哪个或者哪几个专家去计算。这样做的好处很直接——模型总参数量可以很大但每次前向计算只用其中一小部分参数推理成本大幅下降。举个例子假设你有一个包含8个专家的MoE模型每个专家是一个独立的FFN前馈网络层总参数量可能达到几百亿。但因为路由模块每次只激活Top-2个专家所以实际参与计算的参数量可能只有全量参数的几分之一。这就相当于你养了一支几十人的专家顾问团但每次遇到具体问题只需要喊其中最对口的两个人过来处理其他顾问在旁边喝茶休息。省人力省电费而且处理效果还不差。GPT-4具体用了多少个专家、路由怎么设计官方一直没有公开细节。但已经开源并且大家非常熟悉的Mixtral 8x7B结构就很典型——8个专家每次输入激活2个。它总参数量约47B但推理时的计算量只相当于一个12B左右的稠密模型。这个数字对比非常直观也是MoE最吸引人的地方。从部署和推理的角度来看MoE带来的挑战也很明显。最大的一个就是“显存/存储带宽压力”。虽然计算量小了但模型总参数量并没有减少——所有专家的权重都得存在内存里。推理时虽然只算2个专家但想要算哪个专家总得先把那个专家的权重从内存搬到计算单元里吧如果搬到速度跟不上计算的消耗速度整个系统就会卡在“等权重”这件事上。这也是为什么MoE模型在GPU上跑的时候特别吃显存带宽H100、A100这类带宽怪兽在这方面有明显优势。但这恰恰也是FPGA的机会窗口。FPGA的存储层次是可以定制的片上BRAM/URAM可以按需分配给不同专家外部DDR的带宽分配也可以根据访问特征做针对性优化。相比固定架构的GPUFPGA在处理“冷门专家和热门专家访问频率严重不均”这类问题时多了很多可操作的空间。这是后文要展开讲的重点。我的理解是MoE的本质是用“参数稀疏化”换“计算稀疏化”。它不减少模型存储量只减少计算量。理解了这个本质后面再做硬件映射的时候思路就不会跑偏。2. 为什么有人愿意用FPGA做MoE推理——GPU之外的另一种解法聊FPGA实现MoE之前先要搞清楚一个问题现在大模型推理基本是GPU的天下FPGA在这里面到底图什么2.1 GPU方案的痛点在哪GPU跑MoE推理最大的痛点是带宽和访存模式不匹配。前面说了MoE每次激活少数专家但为了找到该激活哪些专家路由模块要对输入和所有专家做个相似度计算这个计算量相对不大但它决定了后续的访存行为。问题是GPU的线程模型和数据并行方式是相对固定的SM流式多处理器调度以warp为单位。当路由结果导致不同输入样本选择了不同专家时同一个warp里的32个样本可能被路由到4个不同的专家上这时候就产生了严重的访存发散memory divergenceSM的效率会大打折扣。另一个痛点是动态性。MoE的专家负载天然不均衡有的专家可能会被频繁选中hot expert有的可能长期“失业”。GPU上处理这种动态负载均衡需要显式的通信和重新分配逻辑软件栈要做的额外操作很多稍不留神就会引入大量开销。还有一点是部署场景。很多应用并不在数据中心而是在边缘设备、嵌入式系统、工业控制现场。这些场景不一定有条件塞一块几百瓦的GPU但有功耗限制、时延要求、数据安全隔离需求。FPGA在这种环境下的适用性其实比GPU更强。2.2 FPGA能够提供的独特价值FPGA做MoE推理我认为核心优势有三个。第一存储层次可定制。GPU的缓存层次是设计死的L1、L2、显存程序员能做的只是尽量让数据访问贴合这个层次。FPGA的片上存储是可编程的BRAM/URAM你可以按照专家粒度划分存储块甚至给热门的专家分配更大的片上缓存空间让它们的数据尽可能留在离计算单元近的地方。这种“存储跟着计算走”的能力是GPU做不到的。第二访存调度可以做到极细粒度。FPGA里你可以为DDR控制器设计专门的请求调度逻辑针对路由结果做批量的权重预取prefetch。比如路由模块刚算出结果还没有正式开始专家计算的时候FPGA的DMA逻辑就可以根据路由结果去DDR里把对应专家的权重搬上片。这个“提前量”如果设计得好能把DDR的带宽利用率拉得很高。第三数据通路可以旁路掉模型中间的无关数据流动。MoE模型除了专家网络还有注意力层、归一化层、残差连接等。在GPU上这些层是同一个计算阵列按指令序列执行的在FPGA上你可以为每个层设计专门的数据通路让它们像流水线一样并行工作——注意力在算的时候路由模块已经在算下一个token的专家分配了专家计算的结果也在往回写了。整个推理过程真正变成一条流水线而不是GPU那种“一个大kernel接一个大kernel”的模式。当然FPGA也有劣势比如开发周期长、频率低、通用性差。但如果针对某个固定模型做长期部署、批量出货FPGA的优势就会随着功耗、面积、成本摊薄而逐渐显现。2.3 什么样的MoE场景适合FPGA并不是所有MoE推理都适合拿到FPGA上做。根据我接触过的项目下面几类场景比较现实模型结构固定且已知比如公司内部自研的MoE模型结构不会再频繁变动。推理批量比较小时延要求高典型如自动驾驶、工业实时控制里的在线推理。带宽和功耗严格受限比如单板功耗限制在20W以内还要跑一定规模的模型。数据不出设备对安全隔离有要求FPGA的物理隔离特性天然合适。反过来如果是要做大规模并发推理、高吞吐的云侧服务那GPU还是理性选择。FPGA在这个场景里打正面胜算不大。我的判断是FPGA做MoE当前不是要取代GPU而是在那些GPU“使不上劲”的地方把它补上——低功耗、低延迟、定制访存、安全隔离。想清楚定位再谈实现。3. 从算法到硬件MoE模型映射FPGA的整体设计思路3.1 拆解MoE推理的计算图要把一个MoE模型搬到FPGA上第一步不是写代码而是把这个模型的计算图拆开看清每一部分的数据流和控制流。一个典型MoE层的推理过程大概分成这样几步输入向量x进入MoE层。路由模块计算每个专家的得分得到logits。对logits做softmax或稀疏归一化取Top-k个专家。根据路由权重把输入x分别送入选中的k个专家网络。每个专家输出结果乘以对应的路由权重。所有被选中的专家结果做加权求和得到该MoE层的输出。这个过程里第1、2步是纯计算密集型第3步是控制密集型第4到第6步是计算访存混合。在FPGA上做设计的时候要把这几步区分对待。路由计算第2步本质是一个矩阵向量乘输入向量和每个专家对应的中心向量/权重做内积。这个计算量并不大但它的结果决定了后续几乎所有行为必须做得足够快。在FPGA里这一步可以放在一个专门的路由计算单元里用并行乘加阵列实现。如果专家数量是8个或者16个这种规模的矩阵乘对于FPGA来说非常轻松几个周期就能出结果。Top-k选择第3步在FPGA里实现也有讲究。8个取2个直接比较就能做到如果专家数量扩展到64个、128个甚至更多就需要用硬件友好的Top-k排序网络。注意这里不需要精确排序所有专家的得分只要找到前k个所以可以用分块比较的思路把专家分成若干组先各取top再合并比较能节省大量硬件资源。专家计算第4步到第6步是数据量和计算量的主体。每个专家就是一个FFN层里面是两层线性变换激活函数。在FPGA里实现线性变换可以用矩阵乘加速器做激活函数一般用查找表实现比如SwiGLU这类激活直接用LUT分段拟合。关键是多个专家之间是并行还是轮转这要看硬件资源来定。3.2 硬件架构的三层划分把MoE层的计算流拆清楚之后硬件架构可以按三个层次来组织第一层路由决策层。对应前面的第1到第3步负责快速产生路由结果。这一层对计算量要求不高但对时延敏感必须做得浅尽量在几十个周期内出结果。第二层专家执行层。这是模型的主体由若干个专家计算单元Expert UnitEU组成。每个EU内部包含一组乘加阵列、激活函数查找表、输出累加逻辑。EU的数量通常不等于专家数量而是远小于专家数量——原因很简单FPGA资源有限而模型推理每次只激活Top-k个专家只要保证至少k个EU在同时工作就够了。第三层全局调度与存储层。包括路由结果缓存、输入分发网络、专家权重预取器、结果汇聚与加权求和单元。这一层起到“后勤部”的作用负责让所有数据在对的时间到达对的地方。这三层之间的关系是这样的路由决策层得出结果后触发全局调度器调度器一边给对应EU下发计算指令一边从外部DDR预取专家权重。EU算完之后结果汇总到加权求和单元结合路由权重做最终输出。这个过程如果设计成流水线不同token就可以在不同阶段同时被处理吞吐量会非常可观。3.3 为什么FPGA实现MoE不能照搬GPU思路这条我想单独强调一下。很多做过GPU CUDA开发的工程师第一次接触FPGA的时候习惯性地想把GPU的编程模型映射到FPGA上——比如想象一堆并行的线程每个线程负责一个数据元素。但在FPGA里这种“单指令流多数据流”的抽象并不好用。FPGA更自然的抽象是“多个专门化的处理引擎每个引擎负责一个小任务引擎之间通过FIFO/BRAM互联”。具体到MoEGPU的做法是把专家计算当作一个大kernel执行内部用大量线程并行。FPGA的做法则是把专家计算拆成几个并行的硬件流水线每个流水线针对一个专家、处理一个token流。你会发现从算法到架构的映射方式完全不同GPU是“数据并行”FPGA是“任务并行流水线并行”。想清楚这一层后续的RTL和HLS编码才不会走大弯路。设计MoE的FPGA架构最重要的不是把每个数学运算都想得多么复杂而是把“路由→调度→计算→汇聚”这条控制路径想清楚。控制路径通了数据通路就是水到渠成的事。4. 关键模块实现细节——路由、专家计算与存储调度4.1 路由模块的FPGA友好实现路由模块可以用一个简单的矩阵乘实现假设输入向量维度为d专家数量为N那么路由权重矩阵是N×d。输出分数的计算就是input(1×d) × weight(N×d)得到N个logits。FPGA实现这一步可以直接用一个乘加阵列。比如d4096N8那么这步要做8×4096次乘加运算。以FPGA的DSP资源来算如果器件上有2000个DSP每个DSP可以做一个乘加2000个DSP并行计算的话只需要约16个周期就能算完4096维的点积这个速度非常快。代码层面如果用HLS高层次综合来写这个路由模块非常简单本质上就是一个矩阵向量乘。但要注意HLS综合时一定要显式控制流水和数组分割否则生成的电路并行度会很低。4.2 专家计算的硬件组织方式专家计算是MoE里计算量最大的部分。以Mixtral 8x7B为例每个专家的FFN输入维度是4096中间隐藏层是14336输出维度回到4096。单看这个FFN两层矩阵乘的计算量非常大。但FPGA资源有限通常不会把整个FFN的权重全部放在片上而是采用“分块计算数据流复用”的方式。一种常见的做法是将FFN的第一层矩阵按输出通道分成若干block每次加载一个block的权重到BRAM完成一遍部分计算再加载下一个block。这样虽然权重搬运次数变多了但BRAM占用大幅降低。这种做法在FPGA里叫“权重分块流式处理”非常适合大矩阵乘。另一个关键设计是EU之间的并行与串行粒度。假设系统里有4个EU而模型每次激活2个专家那么可以一次把2个专家分别分配给2个EU并行计算另外2个EU留作流水线重叠处理下一批token。如果模型每次激活的专家数量更大比如Top-8那就需要更多EU。在设计EU的过程中我的经验是要给每个EU配置一块独立的权重缓存缓存大小应该能容纳单个专家全部或者大部分权重。这样做的原因很简单专家切换时的存储重加载开销非常大如果每个EU只能容纳专家的一小部分权重每次切换专家都要从DDR重新灌权重时间浪费严重。片上缓存越大专家切换越顺滑。4.3 存储系统的分层调度MoE的存储调度是整个设计里最容易出问题的部分。专家的权重普遍很大无法全部放在片上所以只能分层管理。第一层是片上BRAM/URAM用来缓存当前或者下一批正在计算的专家权重。每个EU配一个权重缓存区命中的专家权重直接从缓存区读取不用去DDR。第二层是片外DDR存放所有专家的完整权重。当路由结果出来之后预取器判断所选的专家权重是否在片上缓存中如果不在就发DDR读取请求。但这里有个不好处理的问题DDR的带宽是有限的。假设DDR带宽是25.6GB/s4个DDR4-3200通道64bit专家权重大小是2GB那么把整个专家数据从DDR搬到片上需要约80ms。如果每个数据请求都等这么久推理速度会非常慢。所以预取策略很关键。一个可行的策略是“提前两个token预取”。路由模块在计算当前token的专家选择时同时根据历史信息和输入特征的连续性强弱预测下一两个token可能激活的专家提前发起权重预取。对于输入变化比较平缓的任务比如视频帧流、传感器时序数据预测命中率会很可观。这种做法虽然增加了一点控制复杂度但能有效掩盖DDR访问延迟。4.4 数据格式与量化选择MoE模型在FPGA上通常要配合量化来做否则带宽和资源都会吃紧。一般来说路由模块对精度要求相对较高建议保留int8或fp16因为它要做softmax和sort误差容易被放大。专家权重的线性层可以用int8量化少数对精度敏感的层可以保留int16。激活值建议用int8如果模型是训练后量化PTQ需要做一定的校准集验证。在具体实现时int8的乘加在FPGA上用的是DSP48E2的25×18模式即用DSP做低精度乘法。要注意的是DSP的输出位宽有限累加过程中要防止溢出。通常做法是每累加一定数量就做一次截断和饱和处理不能等到最后统一处理否则数据位宽撑爆了。关于量化我的实际体会是可以先跑一遍浮点仿真统计中间激活值的动态范围再决定定点位宽和缩放因子。想当然地全模型一刀切使用8bit量化往往会在路由计算和softmax这个环节翻车。5. 用HLS还是RTL写MoE——工程选型的实际考量5.1 HLS的开发效率优势说实话纯RTL写一个完整的MoE推理系统工作量相当大。路由部分还好但专家计算里的矩阵乘、数据搬运、流水控制、状态机协调如果全部用RTL手工设计没有几个月下不来。用HLS比如Vitis HLS确实能显著提速。HLS适合的模块是那种数据流清晰、计算密集、控制相对规整的地方。典型的比如专家FFN里的矩阵乘、激活函数、量化/反量化逻辑。这些模块用HLS写C/C代码量和可读性都远优于RTL综合出来的性能在优化得当的情况下也能接近RTL的八成以上。5.2 RTL依然不可替代的部分但MoE系统里有一块东西HLS不太好搞定那就是路由到专家之间的动态调度逻辑。这个部分涉及条件分支、动态选择、不同数据来源的竞争仲裁用HLS描述起来非常别扭综合后的控制逻辑也常常不够精细。我的做法是把这部分用Verilog手写做成一个专门的路由调度状态机。如果你采用的是纯HLS方案比如使用Vitis HLS的数据流dataflow模式那么一定要特别关注任务之间的同步。MoE的多个专家是条件执行的——不是所有专家都被激活如果用dataflow把所有专家都摆成并行流水线HLS工具往往会做很大的资源冗余因为工具提前无法确定哪些分支会被跳过。这种情况建议把专家执行做成一个动态分发的任务通过控制信号来启动/停止对应的计算任务。5.3 一个可行的混合编码流程我建议的流程是这样的用HLS编写专家FFN计算核、路由矩阵乘、激活函数这三类计算密集模块每个模块单独工程综合生成IP。用Verilog编写顶层系统包含路由调度状态机、crossbar分发网络、权重预取DMA、结果汇聚逻辑。将HLS生成的IP以RTL的形式集成进顶层工程通过AXI-Stream接口互联。用UVM或定向测试分别验证各模块再运行完整的端到端推理仿真对比Python浮点模型输出误差控制在预期范围内。在FPGA板上做原型验证实测资源占用、功耗、吞吐率和时延。这套流程兼顾效率和可维护性是比较推荐的工程路径。如果团队HLS经验不深也可以做模块测试但要留足集成和排错的时间。我见过不少团队一上来就把所有东西都用HLS写结果遇到动态调度就在那儿绕来绕去最后返工。建议从第一天就想清楚哪些归HLS、哪些归RTL分工明确才能减少返工。6. 常见问题与调试技巧——FPGA上跑MoE的坑6.1 路由结果与权重加载延迟不匹配这是最常见的问题。路由模块很快就算出了结果但权重预取需要几十上百个周期才能完成于是专家计算单元只能在等待中空转整个系统的吞吐率被打回原形。排查思路先确认片上缓存命中率如果命中率太低毫无疑问DDR会成为瓶颈。解决办法要么增大EU内的权重缓存要么优化预取策略用“下一批预测”来提前预取。现场调试时可以在路由结果寄存器旁边加一个计数器记录“路由完成到权重就绪”之间的等待周期数观察瓶颈在哪里。6.2 专家负载不均衡导致部分EU空闲理论上如果路由结果分布均匀4个EU就能撑起8选2的负载。但实际上输入数据往往有偏置某些专家被频繁激活另一些几乎闲置。如果一段时间内连续激活的是同一批专家那么未分配到的EU就只能空转浪费资源。处理方式有两种。一种是“动态EU分配”——哪个专家被选中就把它分配给当前空闲的EU而不是把EU固定绑定到专家编号。这样虽然增加了调度复杂度但能有效提升EU利用率。另一种是“批量重排”——在数据流中插入一个重排缓冲攒够一定数量的token后再统一做路由决策尽量让同一批次的token分配到不同专家提升并行度。后者实现更简单适合批量预处理场景。6.3 softmax与Top-k的动态范围溢出路由模块里的softmax涉及指数运算在定点实现时指数很容易放大数值。如果输入logits范围比较大softmax计算中间结果可能超过预定的定点位宽导致结果不正确。我踩过这个坑之后的修复方式是把softmax变成“减最大值再算指数”的形式。先找到所有logits的最大值并减去再做指数和归一化。虽然多了一轮比较和减法操作但数值稳定性和位宽控制会好很多。Top-k排序时也建议用一个“比较-筛选”的硬件友好实现别用软件那种全排序后再取前k硬件代价太高。6.4 仿真可以过、上板就挂这种问题几乎每个FPGA工程师都遇到过。最常见的两个原因时序约束没加全、跨时钟域信号没做同步处理。MoE系统里往往有多个时钟域比如DDR接口的时钟、Expert计算单元的高频时钟、路由模块的较低频时钟。跨时钟域的信号如果没用同步器两级触发器或者异步FIFO处理仿真里可能能过上板就会随机出错。另一个常见问题是综合工具默认不会自动约束BRAM的输出寄存器导致时序收敛困难。解决办法是在约束SDC文件里显式定义所有关键路径并且对每个跨时钟域的接口打上set_clock_groups或者使用FIFO做同步。6.5 资源估算不足导致布局布线失败很多人在规划阶段就预估资源但MoE这种系统的资源估算波动很大。比如矩阵乘的DSP使用量相对好估但数据分发网络、跨bar、FIFO的LUT/FF开销往往会超预算。建议在架构设计阶段就预留20%~30%的资源余量尤其是LUT和BRAM。如果估算太满综合布线工具会在布局阶段报错到时候修改架构的成本非常高。给一个实际的撬门技巧在Vivado里先做一次综合看看LUT/FF/DSP/BRAM的初步占用率再对照设计预期。如果某类资源占用超过了70%就尽早考虑优化策略——要么减小并行度要么调整量化位宽别等到布局布线失败再回头改。7. 未来扩展方向——从单板推理到多板互联如果单块FPGA的资源不足以容纳更大的MoE模型一个很自然的扩展方向是多板卡协同。FPGA之间的互联方式常见有几种通过PCIe连接主机、通过以太网/光口互联、通过自定义的GPIO/Aurora协议组成阵列。多板MoE的思路和GPU做模型并行类似——按专家维度切分。比如两块FPGA各自存放一半专家的权重。路由结果如果是本板的专家直接本地计算如果是另一板上的专家就要把中间结果通过互联发送过去。这里核心的问题是通信开销。每次跨板传输的数据量如果太大通信时延就会抵消掉并行收益。一种常见的做法是按token流水并行——两块板分别处理一批不同的token只有当batch内token被路由到了对方板上的专家时才做通信。由于MoE路由是稀疏的跨板通信比例通常不会太高这种方案在批量较大的场景下可行。还有一种做法是把专家复制到多块板卡上按数据并行方式处理这样路由结果在本地都能找到专家不需要跨卡通信。代价是每块板都要存完整的模型权重对存储容量要求高。我个人更看好前一种——按专家切分稀疏通信的方向。它更贴合MoE模型本身的特性而且随着FPGA开发板和SerDes带宽的不断提升跨板通信的开销会继续下降。8. 一些实际测试数据和个人经验总结最后分享几个我自己实测观察到的数据点不一定代表所有平台但可以作为参考。在一款中等规模的FPGA大约25.6万个LUT1200多个DSP5MB左右URAM上我做过一个简化版的MoE层测试专家数量8隐藏维1024激活数2。在int8量化、200MHz主频、单DDR4通道的情况下单层推理的时延大约是30~40微秒吞吐率能到每秒钟几千个token。对比同样逻辑在嵌入式GPU上跑时延差不多但FPGA的功耗只有GPU的大约三分之一。如果把专家数量放大到32个模型的参数量增加DDR带宽压力立刻凸显。实测发现如果专家权重全都放在DDR里预取命中率低于50%时系统几乎会被DDR等待卡死吞吐率下降一个数量级。这个时候增加片上缓存比提升DDR频率更有效。把每个专家的权重缓存从原来的25%提升到50%命中率提升到80%以上吞吐率立刻回到正常水平的六成以上。这些经验说明什么说明MoE在FPGA上能不能跑顺瓶颈往往不在计算而在存储。谁把权重喂给计算单元喂得又快又准谁就能赢。另一个经验是如果你是这个领域的新手想要动手实验不要一开始就奔着大模型去。可以先做一个很小规模的博客级项目一个2到4个专家的MoE层跑在入门级的FPGA开发板上先把路由、调度、汇聚这条链路打通。等你把这一条链路吃透了再去升级专家数量、模型维度、DDR带宽就有了底气。说实话MoE的硬件实现并没有特别高深莫测的东西它更考验的是对系统级的理解和耐心调优。只要一步一步踩实了最后做出来的系统性能不会差到哪里去。