ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI芯片软硬件协同设计:从架构定义到流片部署的工程实践

AI芯片软硬件协同设计:从架构定义到流片部署的工程实践 1. 从能跑就行到跑得高效AI芯片软硬件协同设计的核心命题聊到AI芯片的软硬件设计很多刚入行的朋友第一反应是硬件堆算力软件调API两边各干各的最后拼在一起能跑通就算交差。这种思路在早期原型阶段勉强能用但一旦进入量产部署问题就会像多米诺骨牌一样接连倒下——模型精度掉点、吞吐量上不去、功耗超标、散热压不住最后发现根因往往不是某一侧做得差而是软硬件之间那条缝没对齐。我做了十多年芯片相关的系统设计踩过最多的坑几乎都集中在这条缝上。AI芯片和传统通用CPU最大的区别在于它的价值高度依赖特定计算模式的效率而计算模式又由上层模型和框架决定。这意味着硬件设计不能闭门造车软件栈也不能只做翻译层。两者必须从架构定义阶段就坐在一起围绕同一组工作负载特征做联合优化。这就是**软硬件协同设计HW/SW Co-Design**的本质。这篇文章面向三类读者一是正在做AI加速器架构定义的硬件工程师二是负责推理框架和算子库的软件工程师三是想理解AI芯片全栈设计逻辑的技术管理者。我会从计算模式、数据流、存储层次、编译映射、精度策略几个维度把软硬件设计里那些必须一起想的问题拆开讲清楚并给出可落地的设计取舍方法和实测经验。全文不涉及任何具体厂商的敏感信息只谈通用工程方法。2. 先搞清楚芯片要伺候谁AI工作负载的计算模式拆解2.1 矩阵乘为什么成了绝对主角AI芯片设计的第一件事是搞清楚目标工作负载的计算模式。当前主流神经网络里无论是卷积、全连接还是注意力机制拆到最底层几乎都能归约为**矩阵乘法GEMM**及其变体。卷积可以通过im2col或Winograd变换转成矩阵乘注意力里的QKV计算本身就是矩阵乘前馈网络更是纯矩阵乘堆叠。这个事实直接决定了硬件设计的方向把矩阵乘的吞吐和能效做到极致就抓住了主要矛盾。所以你会看到几乎所有AI加速器都在堆MAC乘累加阵列从128x128到256x256甚至更大。但这里有个容易被忽略的点——矩阵乘的维度是变化的。Transformer里的矩阵可能是[512, 768] x [768, 2048]而某些卷积层可能是[1, 64] x [64, 256]。硬件阵列如果只针对某一种形状优化遇到其他形状就会出现严重的利用率下降。我在实际项目中见过一个典型案例某加速器设计了256x256的脉动阵列跑大矩阵时利用率能到90%以上但跑batch size为1的推理任务时利用率直接掉到15%以下因为阵列的大部分行和列都空着。这不是硬件设计错了而是没有充分考虑实际部署中batch size的分布。2.2 算子形状分布决定了阵列尺寸的取舍设计阵列尺寸时不能只看峰值算力要看目标场景下算子形状的加权分布。做法是拿真实业务模型跑一遍profiling统计每个算子的M、N、K维度以及它在总计算量中的占比然后画一张分布图。假设你的目标场景是云端推理主要跑BERT类模型统计下来发现80%的计算量集中在K768或1024、N3072或4096的矩阵乘上那阵列的列方向就可以优先匹配这些N值。如果目标场景是边缘端模型以MobileNet为主卷积核多为3x3、通道数64到256那阵列设计就要考虑小K维度的效率。这里有个经验公式可以参考阵列边长取目标算子维度分布的中位数附近同时保证对最小常见维度的利用率不低于30%。比如你的算子N维度中位数是512最小常见值是128那阵列边长取128到256之间比较稳妥。取太大小算子浪费取太小大算子要拆多次增加调度开销。2.3 访存模式比计算模式更容易被低估很多人做AI芯片设计时把90%的精力放在计算单元上结果流片回来发现性能瓶颈全在访存。这不是个例是行业里反复出现的教训。矩阵乘的计算强度Arithmetic Intensity决定了它是计算密集还是访存密集。以FP16的GEMM为例一个MNK1024的矩阵乘计算量是21024^3 ≈ 2.1 GFLOP数据量是31024^2*2字节 ≈ 6.3 MB计算强度约333 FLOP/Byte。这个强度下如果片上存储带宽不够计算单元就会饿死。所以软硬件协同设计里数据复用策略是必须一起定的。硬件侧要决定片上缓存SRAM的容量和带宽、数据在计算阵列中的广播方式软件侧要决定分块tiling策略、数据搬运的调度顺序。两边对不上就会出现硬件觉得软件没喂饱软件觉得硬件带宽不够的互相甩锅局面。3. 数据流架构AI芯片设计里最见功力的地方3.1 三种经典数据流的适用边界AI加速器的数据流架构大致分三类权重固定Weight Stationary, WS、输出固定Output Stationary, OS、行固定Row Stationary, RS。这不是学术概念而是直接决定芯片面积、功耗和编程难度的工程选择。权重固定是把权重锁在PE里输入数据流过适合权重复用率高的场景比如卷积层。输出固定是把部分和留在PE里累加适合K维度大的矩阵乘。行固定是MIT提出的Eyeriss架构思路试图在两者之间找平衡通过行级别的数据复用降低访存。我参与过的一个边缘推理芯片项目最初选了纯权重固定架构因为目标模型以卷积为主。但后来客户要加Transformer支持权重固定的优势就不明显了因为注意力层的权重复用率远低于卷积。最后改成了可配置的混合数据流代价是控制逻辑复杂度上升了约30%但换来了对两类模型的通用性。3.2 数据流选择要和软件分块策略绑定数据流不是硬件单方面能定的它必须和软件的分块策略匹配。举个例子如果硬件是权重固定架构那软件在做算子映射时就应该尽量让同一组权重在片上停留更久把多个输入batch的数据依次送进来。反过来如果硬件是输出固定软件就要优先把K维度切分好让部分和尽量在片上累加完再写回。这里有个实操建议在架构定义阶段就写一个周期精确的模拟器cycle-accurate simulator把硬件数据流和软件分块策略一起建模。不要等RTL写完再验证那时候改架构的成本已经很高了。模拟器不需要很精细但必须能反映数据搬运的瓶颈和计算单元的利用率。我们团队用Python加SystemC搭的模拟器两周就能跑通主要算子的性能预估比后期返工省了至少三个月。3.3 片上存储层次的设计权衡数据流定了之后片上存储层次就是下一个关键决策。典型的AI芯片存储层次是寄存器文件 → PE本地SRAM → 全局SRAM → 片外DRAM。每一层的容量、带宽、延迟都要和计算阵列的需求匹配。设计时可以用一个简单的带宽匹配公式来估算假设计算阵列有N个MAC每个MAC每个周期需要2个操作数一个来自权重一个来自激活那阵列每周期需要2N个数据。如果PE本地SRAM每周期能提供2N个数据就不会饿死。但实际中往往做不到所以需要数据复用——让一个数据被多个MAC共享。以256x256阵列为例如果每个周期要喂满需要512个操作数。假设权重固定权重从本地SRAM读激活从全局SRAM广播那全局SRAM每周期至少要提供256个激活数据。如果全局SRAM位宽是256字节128个FP16那就需要2个周期才能提供一轮数据阵列利用率直接减半。这就是为什么很多芯片的全局SRAM位宽会做到512字节甚至1024字节。注意片上SRAM的带宽和容量是一对矛盾。容量大意味着面积大、访问延迟高带宽高意味着功耗高、布线复杂。实际设计中通常用多bank并行加crossbar的方式平衡但crossbar的面积会随bank数平方增长一般控制在8到16个bank比较合理。4. 软件栈不是翻译层编译器与算子库的协同设计4.1 图编译器的核心任务是把硬件特性吃透AI芯片的软件栈通常分三层框架适配层、图编译器、运行时。很多人以为图编译器就是把ONNX图转成硬件指令其实远不止。好的图编译器要完成算子融合、内存规划、指令调度、数据搬运插入四件事每一件都和硬件特性强相关。算子融合是最能体现软硬件协同的环节。比如ConvBNReLU这个经典组合硬件如果支持在卷积累加后直接做BN的仿射变换和ReLU激活那编译器就可以把它们融成一个算子省去中间结果的写回和读取。但如果硬件不支持编译器硬融就会引入额外的计算开销。所以融合规则必须和硬件指令集一起定义不能软件单方面决定。我在实际项目里见过一个反例编译器团队为了减少访存把LayerNorm和后面的矩阵乘融合了但硬件没有对应的融合指令结果生成的代码里插入了大量数据搬移指令性能反而比不融合还差20%。后来两边坐下来重新定义了融合边界只融合硬件真正支持的组合性能才回到预期。4.2 算子库的手写优化什么时候必要图编译器能覆盖大部分通用算子但总有一些关键算子需要手写优化。判断标准很简单看这个算子是否占据了总计算量或总访存量的显著比例。通常来说矩阵乘、卷积、注意力这三个算子会占到AI模型80%以上的计算量值得投入人力做手写汇编级优化。手写优化的核心是把硬件的数据流特性用到极致。比如硬件支持权重预加载那手写矩阵乘时就要在kernel启动前把权重搬到片上循环里只搬激活。硬件支持双缓冲那就要在计算当前块的同时预取下一块数据。这些优化编译器自动做往往不够精细手写能再榨出20%到50%的性能。但手写算子库有个维护成本问题。硬件迭代时手写kernel要跟着改模型结构变化时可能要新增kernel。所以我的建议是只对Top 5到Top 10的高频算子手写其余交给编译器。同时建立一套自动化测试确保手写kernel在硬件版本更新后能快速验证正确性和性能。4.3 内存规划编译器最容易被忽视的价值点内存规划是图编译器里技术含量高但存在感低的部分。AI模型的中间张量activation往往很大如果全部放在片外DRAM带宽和功耗都受不了。编译器的任务是通过生命周期分析把可以复用的内存块合并把热点张量尽量留在片上。具体做法是先做一遍张量生命周期分析画出每个张量从产生到消费的时间区间然后做内存复用把不重叠的张量分配到同一块内存最后根据访问频率决定哪些张量常驻片上SRAM哪些放DRAM。这里有个实测经验片上SRAM的分配策略对性能影响极大但很多编译器默认用贪心算法效果一般。我们后来改用了基于整数线性规划ILP的分配方法在张量数量不超过50个时能在秒级求解性能比贪心提升了15%到25%。当然ILP的求解时间会随张量数量指数增长所以实际中会设一个阈值超过就退回启发式算法。5. 精度与能效的博弈量化策略如何影响硬件设计5.1 量化不是软件单方面的事模型量化是AI芯片部署的标配但量化方案的选择必须和硬件一起定。INT8量化在硬件上需要支持INT8的MAC单元FP16需要FP16的MAC混合精度则需要硬件能在不同精度间切换。如果硬件只支持INT8那软件就只能做INT8量化遇到对精度敏感的层比如LayerNorm、Softmax就会掉点。我在一个项目里遇到过这种情况硬件团队为了省面积只做了INT8 MAC结果软件团队发现某些模型的注意力层用INT8量化后精度掉了3个点不得不把这些层回退到FP16模拟性能损失严重。后来下一代芯片加了FP16支持问题才解决。这个教训是硬件精度支持要留有余地至少覆盖INT8和FP16两种最好支持混合精度。5.2 量化粒度与硬件累加器位宽的关系量化的粒度per-tensor、per-channel、per-group直接影响硬件的累加器设计。Per-tensor量化最简单一个张量共享一个scale硬件只需要一个累加器。Per-channel量化每个通道一个scale硬件需要在累加后按通道做缩放增加了后处理逻辑。Per-group量化更细硬件复杂度更高。累加器位宽也是个关键参数。INT8乘INT8的结果是INT16但累加多个乘积后可能溢出。假设累加K1024个乘积最坏情况下需要INT16 log2(1024) INT26的位宽。实际中不会取最坏情况通常用INT32累加器就能覆盖绝大多数场景。但如果做per-channel量化不同通道的scale差异大累加器位宽要相应增加。提示累加器位宽每增加8位面积大约增加15%到20%。所以在精度允许的前提下尽量用INT32而不是INT40或更高。可以通过量化感知训练QAT来约束权重和激活的数值范围降低对累加器位宽的需求。5.3 能效视角下的精度选择从能效角度看精度越低MAC单元的功耗越低。INT8 MAC的功耗大约是FP16的1/3到1/2INT4又是INT8的1/2左右。但精度降低会带来模型精度损失需要在两者间找平衡。实际部署中我通常建议分层量化对精度不敏感的层如大部分卷积层和全连接层用INT8甚至INT4对精度敏感的层如LayerNorm、Softmax、注意力输出保留FP16。硬件如果支持混合精度就能在能效和精度间取得较好的平衡。实测下来分层量化相比全INT8精度能提升1到2个点而能效只损失10%到15%。6. 从架构定义到流片软硬件联合验证的实操链路6.1 架构探索阶段的快速迭代方法架构定义阶段最怕的是拍脑袋定参数流片后才发现不对。为了避免这种情况我们团队的做法是搭建一套快速架构探索流程用Python写一个性能模型输入是硬件参数阵列尺寸、SRAM容量、带宽、数据流类型和软件参数分块策略、量化方案输出是目标模型的预估性能和功耗。这个模型不需要周期精确但要在半天内跑完一轮参数扫描。具体步骤是先定义参数空间比如阵列边长从64到512SRAM从1MB到16MB数据流三选一然后用拉丁超立方采样选100组参数组合接着对每组参数跑性能模型记录吞吐、能效、面积预估最后画帕累托前沿图找出在给定面积约束下性能最优的参数组合。这套流程我们跑过多次能把架构探索周期从几个月压缩到两三周。关键是性能模型要足够准误差控制在20%以内就可以指导决策不需要追求100%精确。6.2 RTL阶段的软硬件接口冻结架构定了之后进入RTL实现阶段。这时候最重要的事是冻结软硬件接口包括指令集、寄存器映射、中断机制、DMA描述符格式。接口一旦冻结软件团队就可以并行开发编译器和运行时不用等RTL完成。接口冻结前要开至少三轮评审硬件、软件、验证三方都要参与。评审的重点是指令集是否覆盖了所有目标算子、寄存器是否够用、DMA描述符是否支持所需的分块模式、中断是否支持多任务并发。我见过太多项目因为接口没冻结就并行开发结果后期接口一改软件全部返工浪费几个月。6.3 流片前的联合仿真与性能回归流片前必须做软硬件联合仿真用RTL仿真器跑真实模型验证功能正确性和性能达标。这一步通常很慢一个BERT模型跑一遍可能要几小时甚至几天。所以要用分层验证策略先用小模型如ResNet-18做功能验证确认无误后再用大模型做性能验证。性能回归要建立基线每次RTL改动后都跑一遍确保性能没有回退。回归指标包括端到端延迟、各算子利用率、访存带宽占用、功耗预估。如果某个指标回退超过5%就要定位原因。常见原因包括数据流调度逻辑改动导致复用率下降、SRAM bank冲突增加、指令流水线气泡增多。7. 那些只有踩过才知道的坑一线经验汇总7.1 阵列利用率不是越高越好新手容易陷入阵列利用率越高越好的误区。实际上追求高利用率可能导致调度复杂度飙升反而拉低整体性能。比如为了填满256x256阵列编译器要把小算子拼成大算子拼接过程涉及数据重排和额外搬移开销可能超过利用率提升带来的收益。我的经验是利用率目标定在70%到80%比较合理留出余量给调度灵活性和突发情况。强行追到90%以上往往得不偿失。实测中利用率从75%提到85%端到端性能可能只提升3%到5%但编译时间可能翻倍。7.2 功耗瓶颈常在数据搬移而非计算很多人优化AI芯片时盯着计算单元但实测功耗分布往往显示数据搬移尤其是片外DRAM访问的功耗占比超过50%。一次DRAM访问的能耗大约是一次SRAM访问的100倍是寄存器访问的1000倍。所以减少DRAM访问是降低功耗的关键。软硬件协同的降功耗手段包括增大片上SRAM容量让更多数据常驻、优化分块策略提高数据复用率、用压缩技术减少搬移数据量。我们做过一个对比把SRAM从4MB增到8MBDRAM访问减少40%整体功耗下降25%而面积只增加约15%。这个 trade-off 在多数场景下是划算的。7.3 编译器与硬件的最后一公里问题即使架构和编译器都设计得不错实际部署时还是会遇到最后一公里问题某些算子在特定形状下性能骤降、某些模型因为控制流导致编译失败、某些batch size下内存不够用。这些问题往往在实验室测不出来只有真实业务跑起来才暴露。应对方法是建立真实业务负载的持续测试集覆盖不同模型、不同batch size、不同输入形状。每次软硬件更新后都跑一遍记录性能变化。同时保留一个逃生通道对于编译器搞不定的算子允许回退到CPU或手写kernel保证业务能跑通。这个逃生通道虽然不优雅但在实际部署中能救命。7.4 精度调试的排查顺序模型部署后精度掉点是常见问题排查要讲顺序否则容易大海捞针。我的排查顺序是先确认量化误差把量化模型和浮点模型逐层对比找出误差最大的层。再查算子实现对比硬件算子输出和CPU参考实现确认是否有计算错误。然后查数据布局确认张量的内存布局NCHW vs NHWC在软硬件间是否一致。最后查数值稳定性检查是否有溢出、下溢、累加顺序导致的精度损失。这个顺序能把排查时间从几天压缩到几小时。我见过最隐蔽的精度问题是累加顺序不同导致的硬件为了并行把累加拆成多路最后合并时顺序和CPU不一样浮点误差累积后导致掉点。这种问题只能通过逐层对比发现。8. 写在最后一些个人体会做AI芯片的软硬件设计这些年最大的体会是这不是两个团队的合作而是一个团队的两只手。硬件工程师要懂模型和框架软件工程师要懂流水线和存储层次两边用同一种语言讨论问题才能做出真正好用的芯片。如果让我给刚入行的朋友一条建议那就是尽早搭建联合仿真环境尽早跑真实模型尽早暴露问题。实验室里的漂亮数据不代表量产能用只有真实业务负载跑通了才算真正做成了。另外别怕改架构架构探索阶段改一次的成本远低于流片后改一次的成本。多花两周做参数扫描可能省下半年的返工。这个领域变化很快模型结构在变硬件工艺在变但软硬件协同设计的基本逻辑没变让数据流动得最少让计算单元吃得最饱让精度和能效平衡得最好。抓住这三条大方向就不会错。
返回列表