4、协同计算架构设计:异构计算平台(CPU+FPGA+GPU)的拓扑结构、数据流设计、任务划分策略
各位同学,今天我们来聊聊异构计算平台的核心——CPU+FPGA+GPU这三兄弟怎么搭伙过日子。
说实话,我最早接触异构计算时,觉得不就是把三个芯片焊在一块板子上嘛。后来被现实狠狠教育了一顿——拓扑结构没选对,数据流卡成狗;任务分不好,性能还不如单颗CPU。嗯,这里面的门道,我今天一次性讲透。
4.1 拓扑结构:三种主流玩法
我个人习惯把异构拓扑分成三类,每一类都有它的脾气。
4.1.1 共享总线型(入门级)
CPU、FPGA、GPU都挂在同一条PCIe总线上。结构简单,成本低。
┌──────┐ ┌──────┐ ┌──────┐
│ CPU │ │ FPGA │ │ GPU │
└──┬───┘ └──┬───┘ └──┬───┘
└────────────┼────────────┘
│
┌─────┴─────┐
│ PCIe总线 │
└───────────┘
我在项目中遇到过这种方案,优点是开发快,缺点是带宽共享。你想想看,FPGA正在疯狂搬运数据,GPU那边要拉模型权重,两边抢总线,谁也别想好过。
4.1.2 直连型(进阶级)
CPU和FPGA之间走PCIe,FPGA和GPU之间走NVLink或CXL。说白了,就是把最频繁的数据通路单独拉出来。
┌──────┐ PCIe ┌──────┐ NVLink ┌──────┐
│ CPU │◄──────►│ FPGA │◄────────►│ GPU │
└──────┘ └──────┘ └──────┘
这种结构的好处是:FPGA做预处理,直接把结果喂给GPU,不用经过CPU中转。延迟能降一个数量级。
我建议做VLA模型推理加速时优先考虑这种。VLA模型里视觉编码器那部分,FPGA做完特征提取,直接通过NVLink塞给GPU做LLM推理,爽得很。
4.1.3 交换网络型(骨灰级)
所有节点通过高速交换网络互联,比如InfiniBand或Ethernet RoCE。适合多节点集群。
┌─────────── 交换网络 ───────────┐
│ │
┌────┴────┐ ┌────┴────┐
│ CPU+FPGA│ │ GPU集群 │
│ 节点1 │ │ 节点N │
└─────────┘ └─────────┘
这种方案我在云端推理集群里用过。说实话,成本高,但扩展性好。如果你要做大规模VLA模型服务,这是终极形态。
4.2 数据流设计:让数据跑起来
拓扑结构定了,接下来就是数据怎么流。我总结了一个口诀:流水线、乒乓缓冲、异步传输。
4.2.1 典型数据流路径
以VLA模型为例,数据流是这样的:
- 输入阶段:CPU接收用户请求(文本+图像),做初步解析
- 预处理阶段:FPGA做图像缩放、归一化、token化
- 推理阶段:GPU运行LLM主干网络
- 后处理阶段:FPGA做beam search、采样
- 输出阶段:CPU组装响应,返回给用户
关键点:FPGA在这里扮演了两个角色——前端的"加速预处理"和后端的"加速解码"。GPU只管做最重的矩阵运算。
4.2.2 乒乓缓冲设计
我习惯在FPGA内部用双缓冲(ping-pong buffer)。一个缓冲区在写数据,另一个在读数据,交替进行。
// 伪代码示意
always @(posedge clk) begin
if (toggle) begin
// 写 ping 缓冲区
ping_buffer[addr] <= data_in;
// 读 pong 缓冲区给GPU
data_out <= pong_buffer[rd_addr];
end else begin
// 写 pong 缓冲区
pong_buffer[addr] <= data_in;
// 读 ping 缓冲区给GPU
data_out <= ping_buffer[rd_addr];
end
end
这样做的好处是:FPGA和GPU可以同时工作,不用互相等。我曾经在项目里没做乒乓缓冲,结果GPU每次都要等FPGA写完才能读,吞吐量直接腰斩。
4.2.3 异步传输与DMA
千万别让CPU参与数据搬运!用DMA控制器。FPGA内部例化一个XDMA IP,直接和GPU的显存做点对点传输。
💡 小技巧:我建议把DMA描述符环设计成256深度,这样CPU只需要每256次传输才中断一次,大大降低CPU负载。
4.3 任务划分策略:谁该干什么活
这是最考验经验的地方。任务分得好,1+1+1>3;分不好,1+1+1<1。
4.3.1 任务划分原则
| 计算单元 | 适合的任务 | 不适合的任务 |
|---|---|---|
| CPU | 控制逻辑、分支判断、动态调度 | 大规模并行计算、固定流水线 |
| FPGA | 固定流水线、低延迟预处理、位宽变换 | 复杂控制流、动态分支 |
| GPU | 大规模矩阵运算、Transformer推理 | 小批量、强依赖的串行任务 |
说白了,CPU管脑子,FPGA管手脚,GPU管力气活。
4.3.2 VLA模型的具体划分
以我最近做的一个VLA模型为例:
- CPU负责:请求调度、token管理、异常处理、结果组装
- FPGA负责:图像预处理(resize、normalize)、视觉token嵌入、KV cache管理、beam search加速
- GPU负责:LLM的self-attention、FFN、softmax等核心计算
为什么这么分?FPGA做KV cache管理特别香。因为KV cache的读写模式固定,FPGA可以用HBM带宽做到极低延迟。GPU做这个反而浪费算力。
4.3.3 动态负载均衡
任务不是一成不变的。我建议在CPU上跑一个轻量级的调度器,实时监控各单元的负载。
// 调度策略伪代码
if (FPGA负载 < 30%) {
// 把部分GPU的预处理任务挪给FPGA
migrate_task(GPU_PREPROCESS, FPGA);
} else if (GPU利用率 > 95%) {
// 启动FPGA的投机解码
enable_speculative_decoding(FPGA);
}
嗯,这里要注意:动态调度不能太频繁,否则调度开销会吃掉收益。我一般设置100ms为一个调度周期。
4.4 实战经验总结
最后,分享几个我踩过的坑:
- 别让FPGA做浮点运算:FPGA做float32效率极低,用INT8或BF16。我见过有人非要在FPGA里做float32的softmax,结果面积爆炸。
- PCIe带宽是瓶颈:实测PCIe Gen4 x16的理论带宽是32GB/s,实际能跑到25GB/s就不错了。如果数据量超过这个,考虑用CXL或NVLink。
- 热数据要就近存放:FPGA的BRAM和URAM很宝贵,只放最热的数据。比如VLA模型的视觉token嵌入表,放BRAM里;模型权重放HBM里。
💡 我的习惯:每次设计异构系统,我都会先画一张数据流图,标出每个节点的带宽需求和延迟要求。然后对着图问自己三个问题:数据从哪里来?到哪里去?中间有没有瓶颈?
好了,这一章的内容就到这里。记住,异构计算不是把三个芯片拼在一起就完事了,而是要让它们各司其职、协同作战。下一章我们会深入FPGA内部的微架构设计,讲讲怎么把VLA模型的计算图映射到FPGA的LUT和DSP上。
公众号:蓝海资料掘金营,微信deep3321