
我最近连续接了三个跟MoE相关的项目咨询有做模型推理加速的有想把手头Transformer模型改成稀疏架构的还有两个是专门来问FPGA能不能跑MoE的。说实话放在两年前FPGA和MoE这两个词放在一起还挺冷门但现在热搜里已经开始出现“gemma4 26b a4b moe部署教程”“yolo moe”这种具体问题了说明大家已经不满足于纸面了解而是真想动手落地。这篇就把我这几轮交流下来觉得最值得说的东西整理一下从MoE架构的核心机制到FPGA实现的算力瓶颈、任务划分、硬件架构设计和调试经验争取用一篇文章讲清楚。1. MoE架构到底在解决什么问题——先搞懂路由、专家和稀疏激活MoE全称Mixture of Experts混合专家模型。这个概念其实上世纪九十年代就有人提过了但在大模型时代才真正火起来。它解决的核心问题很简单模型参数越来越大但每次推理其实用不到全部参数。既然用不到为什么还要全部计算一遍于是MoE的思路就是把一个大模型拆成若干个“专家”Experts每个专家只管一部分输入再配一个“路由”或者叫“门控网络”Router/Gating Network让它决定每个token应该去哪些专家。这里有个关键概念叫稀疏激活。传统Dense模型比如LLaMA 7B推理时每个token都要经过全部7B参数的计算而MoE模型比如Mixtral 8x7B虽然总参数量是46.7B但每个token实际只激活两个专家真正参与计算的参数可能只有12B左右。这就是为什么MoE能用更少的算力跑更大的模型。路由机制是整个MoE的灵魂。它本质上是一个浅层的线性层加Softmax输入是每个token的hidden state输出是该token到每个专家的概率分布。通常采用Top-K策略比如K2就是只选概率最高的两个专家。这个操作在PyTorch里很好写但落在硬件上就有讲究了因为路由结果是个动态的、跟输入数据相关的你不知道每个专家会被分到多少个token这就产生了负载不均衡问题。后续我会详细说FPGA里怎么处理这种动态调度。还有一个大家容易忽略的设计是专家容量Expert Capacity。因为每个专家一次能处理的token数量是有限制的如果一个专家收到的token太多超出容量就要丢弃或溢出。这里有个很典型的工程取舍容量设大会浪费计算资源容量设小会丢信息。FPGA上做MoE加速这个参数直接决定了计算阵列的利用率和buffer深度我后面会给一个实际估算的例子。MoE在近年模型里的另一个趋势是细粒度专家比如把FFN那层的中间维度再拆细变成A4B这种模式——4个激活专家每个专家参数更小。这样做的好处是参数量上去的同时计算量增加得不多但缺点对硬件不友好专家数量变多了比如几百个专家路由决策次数变多每个专家上的并发token更少这对动态调度和存储访问模式提出了更高要求。2. 从算力结构看FPGA做MoE的真正瓶颈——带宽比算力更值钱FPGA能不能跑MoE当然能。但跑得好不好得看你是从哪个维度切入的。我接触过的很多开发者一开始都盯着FPGA的乘加资源DSP单元够不够实际上对于MoE推理来说更核心的瓶颈往往在存储带宽和路由动态性上。先做一个带宽估算。假设一个中等规模的MoE模型单层专家参数合计约2GBK2batch为32每个token的向量维度d_model4096。推理时每个专家要处理的数据量大致是从DDR中读取该专家的权重矩阵然后做矩阵乘。假如这2GB权重每秒要被完整读一遍那么仅这一层就需要2GB/s的带宽这还只是一个Transformer层。实际模型有几十层这个概念乘下来对DDR带宽的需求会非常夸张。这也是为什么现在很多MoE推理卡都在拼命堆HBM带宽。FPGA的DDR带宽有多少以常见的DDR4-2400为例64bit位宽的理论带宽也就19.2GB/s左右如果设计师把频率跑到2400MT/s实际能用的也就60%~70%。一套800美元级别的FPGA开发板DDR带宽基本就在这个量级。而高端GPU的HBM带宽已经到2TB/s以上。所以从“喂饱计算单元”的角度讲FPGA跟GPU比没有任何优势。但这里有个反直觉的地方MoE专家计算强度很低。所谓计算强度就是“每读取一个权重能做多少次乘加”。专家FFN那一层的矩阵乘权重被读取一次通常只能跟一个token的向量做乘加复用率很低这是典型的Memory-bound。这种场景下计算单元利用率上不去DSP阵列再大也是空转。FPGA的价值恰恰不在峰值算力而在于你能把DDR带宽用到极致把每个字节的读取都变成有效计算并利用PCIE与主机之间灵活的数据搬运能力做流水。那么FPGA上设计一块MoE加速卡的正确思路就清晰了不是堆DSP而是先计算这一层到底需要多少外部带宽再决定权重如何切分、缓存如何组织、batch如何调度。先定带宽再定架构最后才是计算阵列的规模。3. FPGA适合做MoE的哪一部分——算力任务划分与硬件架构切分先承认一个现实目前用FPGA跑几十B甚至上百B的MoE模型做端到端推理并不现实。参数量太大DDR装不下PCIE带宽也不够实时换权重。但FPGA在MoE生态里仍能扮演重要角色关键在于任务划分。比较务实的方案是异构切分。主机端的GPU或CPU处理Core Attention部分和大部分Dense层FPGA专门做MoE层中的专家计算和路由分发。比如某模型有12层MoE你可以把其中8层的专家计算卸载到FPGAGPU只算Attention和路由打分。这样FPGA只需存储这批专家的权重带宽压力小一个数量级PCIE的压力也只在层与层之间传输hidden state和路由结果。另一个适合FPGA切入的场景是中小规模模型全量上板。比如手头训练好的一个MoE分类模型总参数量不到500MBembedding之外就是几十个专家FFN这种规模用Virtex或Kintex级别FPGA的DDR就能装下。我做过类似项目一个8专家、hidden512的MoE层在Xilinx ZCU102上能跑到1.1ms的端到端延迟比同板上纯CPU推理快8倍左右。关键是延迟确定性很好没有任务调度抖动适合对实时性敏感的边缘场景。还有一类非常有意思的应用是FPGA做路由预分析和稀疏模式感知。MoE的路由结果在训练好之后其实是有统计规律的某些专家总接收特定类型的token。FPGA可以在运行时实时统计路由分布把热门专家权重固定在BRAM或URAM里冷门专家放DDR按命中率动态调整缓存策略。这种“磨损均衡”式的带宽优化在GPU上不好做因为GPU架构本质上假设所有线程公平访问全局内存但FPGA的缓存完全是你自己控制的可以设计专门的多级缓存策略。至于训练任务我劝你别在FPGA上折腾。MoE训练的负载均衡损失、专家并行、分布式通信这些在FPGA上实现成本极高收益基本为零。FPGA适合做推理、边缘部署和控制流复杂的加速场景不适合做训练。4. 从实际项目出发——一套FPGA MoE推理加速架构的设计推演这里用一个真实做过的项目来推演一遍完整架构配置是8个专家专家FFN维度1024d_model512K2batch32FP16精度。整个MoE层权重约33MBDDR3单通道足够容纳。4.1 顶层数据通路与流水线划分整个加速器分为三个阶段。第一阶段是路由计算用一个小的矩阵乘单元算出32个token各自的专家概率向量并用Top-2模块选出两个专家编号同时算出每个token到各专家的“排序索引”因为同一专家收到的token顺序无所谓但你要保证输出时按原token顺序重组。第二阶段是专家执行分成8个专家并行计算单元Expert Core每个Expert Core里有独立的矩阵乘和激活函数流水线。第三阶段是Combiner把所有专家计算结果按路由索引做加权求和后输出。这里最关键的流水线设计是第二阶段内部不同专家收到的token数量是动态的你不能锁死每个Expert Core必须一次处理固定数量的token。我的做法是给每个Expert Core配一个输入FIFO和输出FIFO路由分发阶段往FIFO里写数据专家执行阶段按FIFO中实际数据量调度计算。这样即使出现某个专家收到20个token、另一个专家只收到1个token的极端情况也不会阻塞其他专家。4.2 专家权重存储与访存策略专家权重的存储方式决定了访存效率。我试过两种方案第一种是把全部专家权重连续存放在DDR里每个Expert Core按自身路由结果去DDR里取对应专家权重第二种是按专家分块存放每块独立地址连续配合多端口DDR控制器让8个Expert Core可以并行读取不同专家权重。实测下来第二种方案更可靠虽然DDR本身是单端口共享的但分块能减少bank conflict配合DDR控制器的多通道特性实际吞吐能提升30%以上。还要注意一个细节专家Proj层的两个矩阵W_up和W_gate最好交替存放因为计算时是先算gate再算up如果交错排列一次burst读就能把两个矩阵对应的行都读回来减少地址切换开销。4.3 Top-2路由选择器的硬件实现Top-2路由在软件里很简单就是排序后取前二。但在FPGA里做32取2的Top-K如果直接排序8位专家打分总共也就8个数其实用组合逻辑就能搞定。但如果专家数量增加到64个甚至128个组合逻辑就会爆炸。这时候要用基数排序的分治策略先分成8组每组内部选出Top-2再做一次8组的Top-2合并。这样一个32输入的Top-2选择器只需3级比较延迟约5ns跑200MHz完全没问题。有个容易踩的坑是Softmax的硬件实现。很多MoE实现里门控打分直接Softmax但如果只是为了选Top-2Softmax的归一化分母是公共的不影响排序结果硬件上完全可以跳过Softmax直接用原始logits做比较省掉指数运算的LUT资源。只有在后面Combiner做加权求和时才需要真正的Softmax值这时再把选中的两个专家对应概率算出来。4.4 相关激活函数与非线性单元的实时化专家FFN里的非线性激活函数如GELU、SiLU是硬件实现里比较繁琐的部分。用查找表LUT的方式做最直接把输入量化到16bit输出查表映射到16bit内存需要128KB用BRAM就能实现。但精度会有一定损失尤其是GELU在两端饱和区误差比较大。我最终用的方案是分段线性拟合把GELU分成6段线性近似每段一个斜率一个截距用两个乘法器和两个加法器就能实现逻辑延迟约4ns最大误差控制在1e-3以内对最终推理精度几乎没有影响。SiLU也就是swish类似分段3段就够。4.5 多级流水的聚合加载与回写逻辑最后一个容易被人忽略的硬件设计点是专家输出回写的Combiner模块。它要做的是把每个token对应的两个专家输出加权求和然后按原顺序放回主序列。这里需要一个Output Reorder Buffer深度要等于batch大小宽度要等于d_model。每个token在路由分发时分配一个唯一序号Combiner按序号写入结果写完一次扫描顺序输出保证与输入序列顺序一致。这个小块逻辑虽然简单但不做好就会造成整个加速器的回写瓶颈。我经历过一次时序跑不过最后发现就是Reorder Buffer的写入控制状态机把读地址和写地址搞混了用ILA定位了大半天。5. 仿真与上板验证的关键节点FPGA MoE加速器最大的调试难题是“动态性”不同输入的路由结果完全不一样你很难用固定testbench覆盖所有情况。所以我的验证流程分三层递进。5.1 第一层CPU参考模型对拍先用PyTorch写一个用FP16模拟的参考模型精确模拟FPGA里的定点行为——包括Top-2选择逻辑、专家FIFO、量化误差、Reorder Buffer的输出顺序。然后跑同一批输入比对FPGA仿真输出和PyTorch输出的误差。这一层能帮你快速定位算法映射中的逻辑错误比如路由索引错位、Combiner加权顺序错、激活函数分段断点错误。5.2 第二层DDR时序下的随机压力测试FPGA仿真里最容易被低估的是DDR时序影响。我用两个测试向量组交替压测一组是路由极度集中所有token全部涌向同一个专家另一组是路由完全均匀。前者测试FIFO溢出处理和总线拥塞恢复后者测试多Expert Core并行度。上板后这组测试暴露过一个问题DDR刷新周期导致的偶发总线等待会让某个Expert Core暂时饥饿后来我在每个专家FIFO前加了深度为64的缓冲才解决。5.3 第三层上板ILA调试上板调试阶段不要上来就抓全信号。我的做法是先把最终输出与CPU参考模型对比确定是哪个阶段出错再用ILA抓对应阶段的控制状态机。FPGAMoE调试中出现概率最高的问题基本集中在三点跨时钟域握手没做干净、DMA描述符地址对齐错误、以及专家FIFO空满标志与总线回压的联动处理。这里有个实用技巧在设计里预埋一组计数器记录每个专家的实际接收token数、FIFO溢出次数、DDR总读字节数。上板跑完一轮后通过AXI-Lite把这些寄存器读出来跟软件端统计对比能快速定位是路由分发逻辑还是存储带宽在拉后腿。6. 关于资源评估和选型的几个实操建议每个来咨询FPGA MoE项目的人几乎都会问同一个问题这活儿到底需要多大FPGA这里给出一个粗略的资源占用经验公式来自我自己几个项目的数据拟合。以一个Expert Core为基准包含一个d_model到专家维度的矩阵乘单元、激活函数、输出投影单元。当专家维度1024、FP16、并行度设计为一次处理16个MAC时单个Expert Core大约消耗8K个LUT、16个DSP、20个BRAM。8个Expert Core就是64K LUT、128个DSP、160个BRAM加上路由模块、Combiner、DDR控制器、PCIE/DMA桥接整体LUT占用在120K~150KDSP约180个BRAM约220个。按照这个规模Kintex-7 K325T可以跑得比较从容资源利用率在60%左右如果换UltraScale的KU040或者Zynq UltraScale系列还能给未来的时序收敛留出余量。如果只用4个Expert CoreArtix-7 200T也能勉强放下但这种片子DDR带宽一般只有16bit宽度CPU侧用AXI DMA搬运数据会成为瓶颈。选型时还有一个容易被忽略的点PCIE硬核数量。因为MoE加速通常不会只插一块卡多块FPGA并行处理不同层是常见做法。如果你有这个规划一定要选带有多PCIE硬核的器件比如Virtex系列或部分UltraScale否则x1或x2的PCIE链路会成为明显的通信瓶颈。另外我强烈建议在选型阶段就先估算DDR带宽而不是先估DSP算力。再强调一次MoE是内存瓶颈型任务你把带宽算明白了很多选择就自动清晰了。7. 最后分享一个亲测有效的调试技巧做MoE FPGA加速最大的噩梦就是专家负载不均衡导致偶发错误这个问题最难的地方在于它既不每次都复现也不是逻辑错误导致的确定性失败。我踩过一次大坑7个专家都正常只剩专家4偶尔输出错值。排查了三天最终发现是DDR控制器在特定bank切换模式下访问延迟超过预期导致Expert Core 4的输入FIFO在读完成之前就开始计算把上一批残留数据算进去了。解决方案是在DMA读取数据前增加一个固定周期的“预充电”等待窗口让总线稳定后再启动计算。这种问题靠仿真很难复现因为仿真里的DDR模型时序相对理想所以上板后一旦出现偶发错误先怀疑总线时序和FIFO空满的握手逻辑别急着怀疑算法精度。另外如果你做的是多batch流式输入建议把相邻batch的输入打散后交错路由而不是每个batch独立路由。这样不仅能降低单batch输出延迟还能减少专家FIFO里的burst流量间接降低DDR访存峰值。这个技巧在纯软件MoE里意义不大但硬件流水线架构下收益非常明显值得一试。