AI芯片软硬件协同设计:从算子特征到编译调度的核心思路与实操
1. AI芯片软硬件协同设计的核心思路拆解1.1 为什么软硬件必须一起设计很多刚接触AI芯片的朋友会有一个误区觉得芯片设计就是硬件工程师画电路、后端团队做布局布线软件是后面再说的“配套工作”。这个思路在传统通用CPU时代勉强能跑通但在AI芯片领域基本等于自杀。原因很直接AI芯片的性能天花板往往不是由某一个乘法器阵列的峰值算力决定的而是由数据在存储层级之间搬来搬去的效率决定的。我举个生活化的类比。你开一家餐厅厨房里有100个厨师算力单元但传菜窗口只有一个食材仓库离厨房要走十分钟。那这100个厨师再快也没用大部分时间都在等食材。AI芯片里的“传菜窗口”就是片上网络和内存带宽“食材仓库”就是片外DRAM。软硬件协同设计的本质就是在芯片定义阶段就把“厨师数量、传菜窗口宽度、仓库距离”这三个变量放在一张桌子上谈而不是硬件定完了再让软件去硬适配。具体来说硬件侧要回答的问题是乘加阵列做多大、支持哪些数据精度INT8、FP16、BF16还是混合、片上SRAM分几级、带宽给多少、有没有专用的数据重排单元。软件侧要回答的是算子怎么切分、数据怎么复用、调度顺序怎么排、量化策略怎么定。这两边如果不在早期对齐最后的结果就是硬件利用率极低标称算力很好看实际跑模型惨不忍睹。1.2 从算子特征反推硬件规格一个合格的AI芯片软硬件设计流程通常是从目标工作负载的算子特征分析开始的。假设我们要设计一颗面向边缘推理的芯片主要跑的是卷积神经网络和轻量级Transformer。那第一步不是画架构图而是把典型模型的算子拆开统计几件事各类算子的计算量占比比如卷积占多少、全连接占多少、注意力占多少算子的数据复用模式是权重复用为主还是激活复用为主张量的形状分布通道数、特征图尺寸、序列长度的典型范围精度需求哪些层可以INT8哪些层必须保留FP16这些统计结果直接决定硬件规格。举个例子如果统计发现3x3卷积占了70%以上的计算量且通道数普遍在64到512之间那乘加阵列的设计就应该围绕这个范围做优化而不是盲目堆一个256x256的大阵列。因为阵列太大小通道数的层就会利用率暴跌这就是典型的“大马拉小车”。注意很多团队在规格定义阶段喜欢对标市面上某款热门芯片的算力数字这是很危险的做法。算力数字是结果不是原因。真正该对标的是目标模型在你的架构上的映射效率。1.3 软硬件接口的早期约定软硬件协同设计里最容易被忽视、但后期代价最大的环节是接口约定。这里的接口不只是寄存器手册那种狭义接口而是包括指令集架构是走VLIW风格的长指令还是RISC风格的标量加向量扩展数据布局约定张量在内存里怎么排NCHW还是NHWC有没有分块同步机制主机和加速器之间怎么握手中断怎么上报量化参数传递scale和zero point怎么从编译器传到运行时再到硬件我踩过的一个坑是硬件团队定义的权重布局是通道优先而编译器团队默认按行优先生成数据结果每次推理前都要做一次额外的转置白白吃掉带宽。这个问题如果在架构评审阶段花半小时对齐就能省掉后期几周的调试和一次流片风险。2. 核心模块的硬件设计细节与实操要点2.1 乘加阵列的微架构选择乘加阵列是AI芯片的计算核心它的微架构选择直接决定了芯片的能效比和面积。常见的方案有三种脉动阵列、广播阵列和树形阵列。它们各有适用场景没有绝对优劣。脉动阵列的特点是数据在阵列中逐拍流动权重预先加载后保持不动激活从一侧流入。这种结构的优势是数据复用率高适合卷积这种权重复用密集的场景。缺点是灵活性差遇到非规则算子比如稀疏注意力时利用率会掉得很厉害。广播阵列则是把激活广播到所有计算单元权重从内存读取灵活性好但带宽压力大。树形阵列介于两者之间通过多级归约减少部分和搬运。选择哪种取决于你的目标工作负载。如果主要跑标准卷积网络脉动阵列是稳妥选择。如果要做通用性更强的加速器支持多种算子那广播加局部脉动的混合结构更合适。实操中有一个关键参数阵列的维度。假设我们选16x16的脉动阵列每个周期可以完成256次乘加。在1GHz频率下峰值算力就是256 x 2 x 1G 512 GOPSINT8。这个数字怎么来的每次乘加算两次操作一次乘法一次加法所以乘以2。这个计算过程在规格定义时必须写清楚避免后期和软件团队对算力的理解产生偏差。2.2 片上存储层级的设计AI芯片的片上存储通常分三级寄存器文件、一级缓存或暂存器、二级缓存。寄存器文件在计算单元内部速度最快但容量最小。暂存器是软件可控的片上SRAM用来存放当前正在计算的输入块和权重块。二级缓存则用来缓冲下一批数据做预取。设计存储层级时核心问题是容量和带宽的平衡。容量太大面积和功耗上去容量太小数据搬运频繁算力利用率下降。一个经验法则是暂存器容量至少要能放下一个完整计算块所需的输入、权重和输出。假设计算块是64通道输入、64通道输出、3x3卷积、特征图8x8那输入需要64x8x8权重需要64x64x3x3输出需要64x8x8。加起来大概几万个字节折算成SRAM面积是可以接受的。带宽方面要保证每个周期能喂饱计算阵列。16x16阵列每周期需要16个激活和16个权重脉动阵列的典型需求那暂存器的读带宽至少要是32字节每周期。这个数字要和SRAM的位宽、bank划分一起考虑不能拍脑袋。实操心得暂存器的bank划分要避免访问冲突。如果激活和权重存在同一个bank同时读取就会冲突导致流水线停顿。通常的做法是把激活和权重分到不同的bank组或者用双端口SRAM。这个细节在RTL设计阶段就要确认后期改代价很大。2.3 数据重排与格式转换单元AI芯片里有一个容易被低估但极其重要的模块数据重排单元。它的作用是在数据进入计算阵列之前把内存中的张量格式转换成阵列需要的格式。比如从NHWC转成阵列内部的通道分块格式或者做im2col展开。这个模块的设计难点在于灵活性。如果只支持固定几种格式转换那遇到新算子就要改硬件。如果做得太通用又变成一个小型处理器面积和功耗都不划算。我的建议是把最常见的几种转换做成硬件通路比如NHWC到分块、im2col、转置其余的交由软件用DMA配合完成。数据重排单元的吞吐量要和计算阵列匹配。如果阵列每周期需要32字节数据重排单元的输出带宽就不能低于这个数。同时重排单元内部通常需要一块小缓冲用来做行缓存或列缓存这块缓冲的大小取决于最大支持的卷积核尺寸。3. 软件栈的分层设计与编译调度实现3.1 编译器前端的图优化软件栈的最上层是编译器前端负责接收训练框架导出的模型做图级别的优化。这一步的目标是减少冗余计算、合并算子、做常量折叠。常见的优化包括算子融合把卷积、批归一化、激活函数合并成一个复合算子减少中间张量的读写常量折叠把推理时不变的权重计算提前算好死代码消除去掉训练专用但推理不需要的节点布局转换消除尽量让相邻算子使用相同的数据布局避免来回转置这些优化听起来是标准操作但在AI芯片场景下有一个特殊考量融合的粒度要和硬件能力匹配。比如硬件如果支持卷积加激活的融合那编译器就应该把这两个算子合并。如果硬件不支持强行融合反而会导致编译器生成低效代码。3.2 算子调度与内存分配图优化之后编译器要做算子调度和内存分配。这一步是软硬件协同设计体现最明显的地方。调度器需要知道硬件的计算资源、存储容量和带宽约束才能生成高效的执行计划。以一个典型的残差块为例里面有卷积、批归一化、激活和加法。调度器要决定哪些算子可以并行、哪些必须串行、中间结果放在片上还是片外、什么时候做预取。如果片上暂存器够大可以把整个残差块的中间结果都放在片上避免反复读写DRAM。如果不够就要做分块计算把特征图切成小块逐块计算。内存分配的策略直接影响带宽消耗。一个常用的技巧是内存复用不同生命周期的张量可以共享同一块内存。编译器通过分析张量的活跃区间把不重叠的张量分配到同一地址。这个优化在模型层数多的时候效果很明显能省下大量内存。3.3 指令生成与硬件映射调度计划确定后编译器后端要生成硬件能执行的指令。这部分和硬件指令集架构强相关。如果硬件是VLIW架构编译器要负责指令打包把可以并行的操作塞进同一个长指令字。如果硬件是标量加向量编译器要生成向量化的循环。指令生成的一个核心挑战是寄存器分配。计算阵列的输入输出都要经过寄存器或暂存器如果寄存器不够就会产生溢出数据被临时写到内存再读回来性能急剧下降。编译器需要做活跃变量分析尽量让数据留在寄存器里。注意指令生成阶段要和硬件团队保持密切沟通。硬件的一个小改动比如增加一个旁路通路可能让编译器的寄存器压力大幅降低。这种改动在流片前做成本很低流片后就只能靠软件绕。4. 常见问题与排查技巧实录4.1 算力利用率低的排查思路算力利用率低是AI芯片落地时最常见的问题。标称100 TOPS实际跑模型只有10 TOPS这种情况太普遍了。排查的时候我通常按下面的顺序走排查项可能原因验证方法带宽瓶颈DRAM带宽不足数据供给跟不上统计实际带宽占用对比理论峰值存储瓶颈片上暂存器太小频繁换入换出看编译报告里的溢出次数阵列利用率算子形状和阵列维度不匹配分析各算子的阵列占用率调度问题算子间依赖导致流水线停顿看时间线找空闲周期精度问题量化误差导致需要回退到高精度对比不同精度下的性能先看带宽因为带宽问题最普遍。如果实际带宽已经接近峰值那说明计算单元在等数据这时候加算力没用要优化数据复用或增加片上存储。如果带宽没跑满再看阵列利用率。如果某个算子的阵列利用率只有30%那要么是形状不匹配要么是调度没排好。4.2 量化精度掉的排查与补救量化是AI芯片提效的关键手段但精度掉点也是家常便饭。排查量化问题我一般分三步第一步逐层对比量化前后的输出。找到误差最大的那一层通常问题就出在那里。常见的敏感层包括第一层卷积、最后一层全连接、以及注意力里的softmax。第二步检查量化参数的统计。如果某一层的激活值分布有长尾用对称量化就会把大部分值压到很小的范围精度自然掉。这时候可以改用非对称量化或者对激活做截断。第三步考虑混合精度。不是所有层都适合INT8。第一层和最后一层通常保留FP16注意力里的softmax输入也建议用FP16。混合精度会增加一些硬件复杂度但精度收益很明显。实操心得量化校准集的选取很重要。用训练集的一个子集做校准效果通常比用随机数据好。校准集要覆盖各种输入分布不能只用一类样本。我见过用纯风景图校准的模型遇到人像就崩了。4.3 多核协同的同步问题高端AI芯片往往是多核架构多个计算核协同工作。这时候同步就成了大问题。核间通信如果走片外内存延迟高、带宽紧张。如果走片上网络又涉及一致性维护。常见的同步方案有两种屏障同步和生产者消费者同步。屏障同步适合所有核一起做一批数据做完再一起做下一批。生产者消费者适合流水线式的处理一个核的输出是另一个核的输入。排查同步问题时我习惯先看时间线。如果发现某个核经常在等那说明负载不均衡或者同步太频繁。解决办法包括调整任务划分让各核工作量接近合并同步点减少同步次数用异步流水线代替全局屏障。5. 软硬件协同的验证与迭代方法5.1 从模型到RTL的联合仿真软硬件协同设计离不开联合仿真。纯软件仿真快但不够准纯RTL仿真准但太慢。折中的做法是用事务级模型做早期验证等架构稳定后再切到RTL。事务级模型的好处是跑得快可以在几分钟内跑完一个完整模型适合做架构探索和编译器调优。缺点是时序不精确不能反映流水线停顿和带宽竞争。等架构基本确定再用RTL跑关键算子验证时序和面积。联合仿真的一个关键是激励的生成。要用真实的模型和真实的数据不能用随机数糊弄。随机数跑出来的性能和真实数据可能差好几倍因为真实数据有稀疏性和分布特征。5.2 性能瓶颈的定位工具定位性能瓶颈需要好的工具支持。最基本的工具是性能计数器记录每个模块的忙碌周期、停顿周期、带宽占用。高级一点的工具有时间线可视化把各模块的活动画成甘特图一眼就能看出哪里是瓶颈。我建议在芯片设计早期就把性能计数器的方案定下来。哪些事件要计数、计数器位宽多少、怎么读出这些都要在RTL里实现。等流片回来再想加计数器就来不及了。软件侧的工具同样重要。编译器要能输出详细的调度报告包括每个算子的执行时间、内存占用、阵列利用率。运行时要有profiling接口能采集实际的性能数据。软硬件工具链打通排查问题才能事半功倍。5.3 迭代优化的节奏把控AI芯片的软硬件协同设计是一个反复迭代的过程。硬件改一版软件跟着调软件发现新需求硬件再改。这个循环如果控制不好项目就会无限延期。我的经验是架构阶段做两到三轮迭代把大方向定下来。然后冻结硬件规格软件在这个规格上做深度优化。如果软件发现硬件有致命缺陷再开一轮小迭代做修补。流片前的最后三个月硬件不再改软件做最终调优和验证。这个节奏的关键是“冻结”这个动作。很多项目失败是因为硬件一直在改软件永远在追。冻结不是说完美了才冻结而是说在信息足够的情况下做决策然后承担决策的后果。留一些遗憾给下一代芯片比无限期拖延要好。6. 一些个人体会和后续扩展方向做AI芯片软硬件设计这些年我最大的体会是这个领域的门槛不在单点技术而在系统思维。硬件工程师要懂模型的算子特征软件工程师要懂硬件的微架构约束两边还要能坐在一张桌子上把问题说清楚。很多项目的问题不是技术不够深而是沟通不够透。另外工具链的成熟度往往比硬件峰值指标更能决定芯片的实际可用性。一颗算力中等但编译器好用的芯片落地速度可能比一颗算力很高但软件难用的芯片快得多。如果团队资源有限我建议在编译器上多投入硬件可以保守一点。后续如果继续深入可以往几个方向扩展一是稀疏计算的支持包括结构化稀疏和非结构化稀疏的硬件加速二是动态形状的支持让芯片能处理变长序列和可变分辨率三是多芯片互联把单芯片的协同设计扩展到集群层面。这些方向都有不少坑也有不少机会。