前两年我深度参与了一个面向端侧的AI芯片项目从架构定义一直跟到流片前的性能回归。这段经历让我对软硬件设计这四个字的重量特别有体会——很多人以为AI芯片就是堆MAC阵列、堆SRAM、把主频拉高但真正拉开差距的是软硬件边界怎么划、算子怎么映射、数据流怎么组织。这篇文章我想把这些年在AI芯片软硬件设计中踩过的坑、验证过的思路和沉淀下来的方法系统性地拆一遍。如果你是做算法、做软件、做系统架构的或者打算入行AI芯片这篇应该能帮你把散点串成线少走几条弯路。1. 从算法到芯片的第一步其实是给硬件划工作量1.1 为什么先定Workload比先选架构更重要我在项目初期犯过一个很典型的错误团队里硬件工程师一上来就照着市面上某头部芯片的规格书抄MAC阵列做多大、SRAM做多少、主频拉多高先定个看起来挺唬人的目标。结果算法团队拿真实模型过来一跑发现完全不对路——一半的算力单元在空转瓶颈全在数据搬运上。正确的顺序应该是先定Workload目标负载再反推硬件结构。所谓Workload不是说我要跑一个目标检测网络这么笼统而是要细化到模型的输入分辨率是多少、每层的输入输出张量形状是什么、卷积核尺寸分布、有没有深度可分离卷积、注意力机制占多大比例、权重是密集还是稀疏、激活值能不能量化到8bit。这些参数直接决定了芯片的体质。我当时拿三个真实模型做了统计结果让我很意外模型类型3x3卷积占比1x1卷积占比矩阵乘占比访存密集算子占比超分模型62%20%3%15%检测模型45%25%15%15%语言模型5%10%70%15%这三组数据放一起结论就非常直接如果芯片主要跑语言模型那MAC阵列架构和存储层级设计要优先照顾大矩阵乘法如果主要跑CNN就要重点优化卷积的数据重用路径。方向选反了后面再怎么调优都是补窟窿。还有一个很容易忽略的维度是延迟要求。端侧实时推理可能要求单帧延迟小于30毫秒这时候很多Offline的批处理优化手段就不能用芯片的流水线设计必须考虑每次推理的冷启动开销。而服务端场景可能更看吞吐量允许你把多个请求拼在一起走大矩阵运算。同一个MAC阵列应对这两个场景的设计方案是完全不同的。1.2 用一道访存算术题理解数据重用是生死线我经常用一道简单的算术题来给新同学讲清楚AI芯片设计的核心矛盾。假设我们要设计一个15TOPS峰值算力的芯片主频1GHz这意味着每秒钟要完成15×10^12次乘加操作折算下来每个时钟周期要完成15000次乘加。每次乘加操作涉及三个数据两个输入、一个输出。如果所有数据都从片外DRAM读取和写回那么每个周期需要读30000个数据、写15000个数据。按8bit计算一个周期要搬运的字节数大约是135KB乘以1GHz的频率需要的片外带宽高达135TB/s。而目前最先进的HBM3E接口带宽也只有1.2TB/s左右差了整整两个数量级。即使所有数据都降到4bit整形需求依然远超出物理极限。所以AI芯片设计的本质不是把MAC做多而是通过硬件结构把喂饱MAC阵列的数据搬运成本降下来。方法就是数据重用——同一个输入数据被尽可能多的计算复用从DRAM加载一次在片上算很多次。拿卷积举例子一个3x3卷积输入通道256输出通道256特征图尺寸是112x112。计算某一个输出位置时需要读取3×3×256个输入激活值、3×3×256个权重值。但同一个输入激活值会被同一输出通道的所有空间位置多次使用同一个权重值会被同一输入通道的所有空间位置多次使用。理论上数据重用的倍数可以达到成百上千倍。硬件的SRAM层级就是为此设计的——把被高频复用的数据块先加载进片上缓存让MAC阵列尽量在片上完成数据访问。1.3 软硬件分工清单谁管什么必须写在架构文档第一页明确了负载和数据重用需求之后接下来要做的不是马上写代码或画电路而是把软硬件边界画清楚。我倾向于用一张责任清单来定义每一行都需要两个团队的负责人签字确认职责硬件负责软件负责基础运算能力MAC阵列、ALU、激活函数单元算子映射、指令生成数据存储与搬运SRAM层级、DRAM接口、DMA引擎内存规划、数据排布、搬运调度指令执行模型指令集架构、流水线编译器的指令调度并行策略多核拓扑、片上互联模型切分、多核任务分配性能兜底峰值算力、带宽规格算子融合、访存优化、利用率提升这张表的潜台词是硬件定了计算的骨架软件定了计算的调度逻辑两边必须知道对方手里的牌。硬件工程师在设计SRAM容量时需要知道软件规划的内存复用策略软件工程师在写算子时需要知道硬件的访存端口数量和bank冲突规则。这种互相知根知底的程度决定了一个AI芯片项目是顺风顺水还是天天扯皮。我们项目前期很多争论本质都是职责清单没写清楚导致的。2. 硬件设计的核心决策点算力骨架怎么搭才不浪费2.1 MAC阵列、脉动阵列和通用ALU的真实取舍芯片的算力骨架按专用程度大概分三个流派通用ALU、MAC阵列、脉动阵列。三者的取舍不是谁比谁先进而是场景匹配问题。通用ALU走的是CPU/GPU路线由大量可编程的算术逻辑单元组成配合复杂指令调度。它的最大优势是灵活几乎所有算子都能跑但代价是控制逻辑和指令解码占用了大量晶体管和功耗。同样一个乘加操作在通用ALU上消耗的能量可能是专用MAC阵列的5到10倍。MAC阵列是大多数AI芯片公司选择的中庸方案每个MAC单元就是一组乘加器和相邻单元共享输入数据总线。它专门为了矩阵乘法和卷积做过优化控制逻辑很薄能效比通用ALU高一个数量级。缺点是对不规则算子的支持差——如果模型里出现大量非矩阵结构的计算MAC阵列的空闲率会很惨。脉动阵列则是更激进的数据流方案让数据在阵列中像流水一样从一个MAC单元流到另一个尽量减少触发器和寄存器堆的访问。它做规则的大矩阵乘法时效率极其惊人但对矩阵形状不规整的算子比如带mask的注意力计算、动态shape的处理效率会大幅下降。我们最后选了MAC阵列为主、加少量专用激活函数单元的思路原因很简单目标模型里既有大量卷积又有Transformer结构脉动阵列的刚性满足不了多变的访存模式。如果你做的是单一场景的极致能效芯片脉动阵列会是更好的选择但要有心理准备去解决它的灵活性短板。2.2 存储层级设计算得快不如喂得饱很多芯片团队把精力全花在MAC阵列面积和主频上结果做出来的芯片在真实模型上跑不出PPT上的峰值算力。原因往往不是计算不够而是存储层级设计拖了后腿。片上存储从离计算单元最近的寄存器堆到中间层SRAM再到片外DRAM每一级的速度和容量差异都在数量级。SRAM的访问能耗大约是DRAM的百分之几但单位面积成本却是DRAM的几十倍。所以存储层级设计的核心问题不是放多少数据而是把什么数据放在哪一级。我实战中的经验公式是这样的先算出每个关键算子的访存-计算比每字节数据能支撑多少次乘加再反推每个层级需要多大的带宽和容量。举例来说一个算子处理的数据块是1MB算力需求是100M次乘加如果预期在10微秒内完成那么MAC阵列需要在10微秒内从SRAM读出100M次操作所需的输入数据。这个带宽需求立刻能判断出SRAM的端口数和bank数合不合理。实际项目里最容易踩的坑是SRAM的bank冲突。单片大SRAM看似容量够但多bank并行访问时如果软件的数据排布策略没跟着硬件走两个MAC单元同时访问同一个bank就会互相等待。我们当时见过最夸张的情况因为bank冲突导致实际性能掉了40%。后续改进方案一部分是硬件加bank数量更多是软件改变数据平铺顺序让连续访问的地址均匀分散到不同bank。另外一个值得留意的细节是DMA引擎的能力。芯片在跑大模型时片上SRAM往往放不下整张特征图需要DMA频繁地从DRAM搬数据。如果DMA只有单向搬运能力而计算单元必须等数据到位才能开工流水线就会出现大量气泡。设计上的正确思路是支持双向搬运和计算-搬运重叠让数据搬运走在计算前面的一个相位等到计算单元需要的时候数据已经在SRAM里待命了。2.3 指令集与片上网络芯片的语言中枢和交通网络AI芯片的指令集和CPU完全不是一个思路。CPU追求单条指令的通用性一条ADD指令能处理各种长度的数据AI芯片追求的是每周期尽可能多地喂饱MAC阵列所以主流选择是VLIW风格的极简指令——一条长指令打包了多个并行微操作包括加载数据、执行计算、存储结果等。设计VLIW指令时要特别注意流水线冒险的问题。因为AI芯片的软件编译器负责在编译期解决指令间的依赖关系硬件已经不需要像CPU那样做乱序执行和动态调度这大大简化了硬件复杂度。但代价是编译器必须对硬件流水线的每一级延迟都了如指掌否则生成的指令会频繁踩坑。我们当时经常遇到的情况是编译器以为数据已经写回SRAM实际上还在流水线里没落地导致计算结果错误排查起来非常痛苦。多核之间的通信结构也决定了芯片能不能扩展到大算力。当计算核数量从几个增加到几十个总线结构会因为竞争访问而性能骤降这时候需要片上网络。我们选的是环路结构理由很务实核数量适中通信模式以相邻核传递中间数据为主环形网络的延迟和面积开销都容易控制。等核数量再上一个台阶更复杂的Mesh网络才会体现出优势。好钢用在刀刃上前期过度设计互联结构同样会浪费宝贵的芯片面积。3. 软件栈不是填完坑就完事它决定芯片上限3.1 AI芯片软件三大件编译器、算子库、运行时调度硬件做出来了等于一个天赋很高但还没学会说话的孩子。软件栈就是教它把天赋兑现在真实模型上。AI芯片软件最少有三大件要落地。编译器是第一个它把某主流深度学习框架导出的中间表示图翻译成目标芯片的指令流。编译过程要做算子融合、循环重排、张量切分等变换。算子库是第二个它用汇编或底层指令手写了一批高效算子内核比如卷积、归一化、矩阵乘、自注意力。为什么有了编译器还要手写算子库因为编译器自动生成的代码在关键算子上往往只能达到手写算子的70%-80%性能而算子库把最核心的30个算子做到95%以上性能整个模型的表现就能被抬高一大截。运行时调度是第三个负责把算子实例提交到具体的计算核上、管理内存池、处理动态shape以及协调多模型并发。看起来不起眼但实际项目里问题最多的就是它。模型推理时经常遇到输入分辨率变化、batch大小抖动如果运行时模块设计得死板一遇到新shape就重新规划内存或者频繁等待同步性能就会雪崩。3.2 算子融合与内存复用编译器里的隐形优化软件栈里收益最直观的优化是算子融合。拿最经典的卷积批量归一化激活函数三个算子来说未融合时卷积的输出要先写回SRAM或者DRAM批量归一化再读出来算写完激活函数又读一次三次访存、三次计算。融合之后批量归一化的参数在编译期就折算进卷积的权重和偏置里激活函数并入卷积计算流水线的最后一级数据在寄存器到SRAM的路径上就直接完成全部处理中间状态完全不落内存访存次数直接减少三分之二。融合不是每次都能成功。我们遇到过因为量化参数在不同算子间传递导致精度轻微下降的案例最后是通过修改融合条件只允许数学上完全等价的融合组合才生效。编译器里还有一个重要优化是数据排布转换硬件经常要求张量按特定顺序存放在SRAM中比如把通道维度放到最后而框架导出的模型通常是通道在第二位直接在硬件上跑会频繁引发地址错位和低效访问编译器要插入数据重排操作但重排本身也有开销做不做、何时做都得用成本模型算清楚。3.3 手写算子在关键模型结构中的不可替代性编译器再智能也替代不了工程师对某个算子深入骨髓的理解。我印象最深的是自注意力模块里的优化过程编译器生成的初始版本在芯片上利用率不到15%主要原因是它把Query、Key、Value当成三个独立的访存主体每算一步都反复搬运中间矩阵完全浪费了片上SRAM的空间。手写版本的做法是先把Query按分块维度切进SRAM里接着调度Key矩阵流式进入乘法阵列让部分结果在片上累加整个计算期间的DRAM访问量仅为原始版本的百分之一。这两者性能差距高达4倍以上也直接决定了模型能不能跑在目标的帧率上。算子库的价值就在这里它把这些经过深度优化的实现固定下来反复打磨成为软件栈的定海神针。4. 实战中的软硬件协同方法论从成本模型到调优闭环4.1 用成本模型在热设计阶段就把瓶颈扼杀软硬件协同不能等到芯片回来了再联调那会儿发现问题代价极高。我们项目采用的是一套从第一天就运行的执行时间成本模型。这套成本模型的逻辑很简单每个算子的执行时间约等于max(计算时间, 访存时间, 同步时间)计算时间用MAC总数除以峰值MAC吞吐访存时间用数据总量除以对应层级的带宽同步时间看多核之间的通信和等待。把这些数字逐层累加就能在架构纸面阶段预测模型的实际帧率。实际操作中我们每周都会跑一轮真实模型的预测性能表对比不同软硬件方案的差异。表格里每一项背后的含义是——如果一个算子的访存时间远大于计算时间那它是访存密集型硬件该加带宽或软件该做数据重用如果计算时间占主导就检查MAC阵列利用率和算子映射方式。算子名数据量预测计算时间预测访存时间实际耗时瓶颈类型卷积116MB1.2ms0.8ms1.5ms计算同步卷积28MB0.6ms1.4ms1.6ms访存矩阵乘64MB3.0ms5.8ms6.2ms访存自注意力32MB2.1ms2.4ms2.9ms访存同步这张表最值钱的地方在于它让我们在设计阶段就看到哪些操作会成为瓶颈然后有针对性地做修改而不是等到流片后拿探针去测。4.2 一个算子到底该进硬件指令集还是交给编译器拆解这是软硬件协同设计里争论最多的问题。把算子做成硬件专用指令性能极高但芯片面积和验证成本也随之上涨。每个专用的硬件加速单元都意味着流片后如果模型结构变了这一块大概率变成死资产。我们的决策原则是二八定律加趋势预判。频率高、未来五年都不会消失的算子——比如卷积、矩阵乘、归一化、Softmax——做成硬件指令。有共性但形态多变的算子——比如各类激活函数、池化、缩放——则以基础指令组合的形式让编译器去组合。至于那些新模型刚出现、未来方向不明的算子宁可暂时用软件模拟跑慢一点也不要急着固化进硬件。实际操作中我曾经强烈主张把某类注意力算子做成专用硬件指令理由是它在最新模型里性能收益很高。结果硬件团队做了评估要增加约10%的芯片面积验证周期延长两个月。后来我们折中成用已有矩阵乘指令加上软件辅助的mask逻辑性能比纯硬件方案低30%但芯片面积换成了4个额外的MAC阵列总收益反而更大。软硬件设计里的很多决策单独看都是技术最优放到全项目里比全局收益结论往往不同。4.3 性能测试的常见误区与实际调优手段芯片回来后性能测试阶段是最容易起内讧的时候——软件说是硬件设计不行硬件说是软件实现太烂。这类争吵的根源往往是对利用率这个指标的理解不一致。很多团队喜欢宣传芯片的峰值算力多高但真实模型跑下来利用率普遍只有30%到50%能在实际负载中做到60%以上就算是相当不错的芯片。我们要做的是把利用率拆开看算力利用率差在哪儿访存带宽利用率又差在哪儿。如果MAC阵列的忙闲不均通常是任务切分或数据排布问题如果带宽利用率不足大概率是DMA调度和数据复用策略不到位。我建议性能测试要做三件事第一准备一组覆盖不同特性的基准模型包括卷积密集型和矩阵乘密集型甚至专门测一下小算子连续执行的场景避免被单一模型的优势掩盖软肋。第二在芯片上开放精确的性能计数器把每个硬件模块的忙闲时间录出来软件侧才能在真正卡住的位置下功夫。第三把实测时间和成本模型的预测时间放在一起对比差异超过30%的地方就是软硬件沟通断点也是后续迭代的突破口。我特别想分享一个亲身教训某次优化后模型的端到端延迟下降了20%大家都很高兴后来细算发现是因为编译器做了算子融合但融合后SRAM的占用率暴涨导致下一层的启动延迟增加如果只看端到端数字根本看不出来。只有把每个算子的时间拆出来逐对比才不会让优化了一处、拖垮了另一处的问题蒙混过关。这也是我一直坚持表格化管理所有性能数据的原因它让软硬件两边有共同语言能完整看到每层的时间明细而不是只盯着最终一个数字。这些都是我在真实项目里反复验证过的思路和工具。如果你手头也在做或者准备做AI芯片建议从设计第一天就建立软硬件联合的工作流算法团队定期给出真实模型的算子分布表硬件团队据此定架构软件团队同步开发成本模型和算子库框架。哪怕每个环节都朴素一点也比各干各的、等到流片回来再互相埋怨要强得多。最后再分享一个最小可行建议动手之前先拿三五个你想跑的真实模型做一遍算子统计和访存分析很多看似高深的设计决策答案会自己浮出来。