AI芯片软硬件协同设计:从算力陷阱到工程落地

发布时间:2026/10/12 1:13:08
AI芯片软硬件协同设计:从算力陷阱到工程落地
一聊AI芯片大多数人第一反应是看算力数字谁家TOPS高谁就更强。可真到做项目的时候决定成败的往往是更虚、也更容易被低估的东西——软硬件设计的配合。我在这行摸爬滚打这些年最深的体会是AI芯片从来不是硬件单方面的事软件栈怎么写、算子怎么进芯片、指令怎么下达每一项都在反向定义这颗芯片长什么样。这篇作为《AI芯片的软硬件设计》系列的第1篇我会从底层逻辑讲起为什么AI芯片不能像CPU那样先定指令集芯片里除了乘法器到底还要有什么软件栈的三层怎么反过来决定硬件规格再拿一次真实项目里的带宽瓶颈复盘完整过一遍最后聊几个我在工程落地时反复踩的软硬接口。想了解AI芯片工作原理、正在做加速器或编译器、或者单纯被各种算力宣传搞到迷茫的朋友这篇文章应该能给你一个比较完整的坐标。1. 从一条指令到一块芯片为什么AI芯片必须先谈软件再谈硬件1.1 传统芯片先定指令集、再各自优化的做法为什么在AI场景下失灵先聊一个老传统。做CPU或GPU最常见的设计流程是先把指令集架构ISA定死把它当作软件和硬件之间的合同。合同签好之后硬件组可以去优化微架构编译器组可以写代码生成两边相对独立只要保证ISA兼容谁都不用等谁。这种模式能跑通是因为通用计算场景几十年没大变样指令集这个抽象层足够稳定。可AI芯片面对的是完全不同的局面。神经网络算法这几年换代的节奏太快卷积网络时代大家盯着NCHW排布和卷积滑窗做优化后来Transformer把矩阵乘法和注意力机制推上台面再后来又冒出MoE、diffusion这些结构。每一次算法演进算子的形状、数据访问模式、对中间结果的处理方式都在变。如果硬件还是先把架构定死等软件栈去适配很可能芯片刚流片出来算法已经换了一茬跑关键模型时的效率惨不忍睹。所以AI芯片领域普遍接受一种做法软硬件协同设计。这个词不是说开几次会、两边沟通一下需求就算协同而是要从立项起就建立一套共同的语言。这套语言的载体通常是一份包含算子形态、数据搬运量、访存模式、时延预算的 workload 清单再配一个能快速估算性能的仿真模型。硬件每个关键参数都要能从这份清单里找到依据软件每个优化动作也要知道硬件能承受什么。没有这套清单所谓协同设计最后都会变成两个团队互相甩需求文档的拉锯战。1.2 协同设计的本质带宽、面积和算子形态的三方博弈把协同设计落到工程语言其实就是三方博弈要做多大算力、留多少片内SRAM、软件允许做多复杂的调度。一个形象的类比是修路和物流调度的关系——路人人都想修得越宽越好可芯片的面积就那么大路修宽了仓库SRAM就得缩小物流司机的调度路线编译器再聪明也没地方放货。反过来调度做得再好路太窄货照样堵在门口。我在不少项目里见过同一种病硬件团队拿着峰值算力当卖点拼命堆MAC阵列SRAM和DMA通道却舍不得给足结果编译器团队发现算子切分怎么做都装不进片内存储只能频繁从外部内存搬数据。跑基准模型时宣称的TOPS连一半都发挥不出来。这就是典型的只谈硬件、不谈软件的设计。真正的协同是在架构阶段就把编译器能干什么、运行时怎么管理内存这些因素当成约束条件而不是等硬件定型后再补救。另外协同设计还有一个很现实的好处能省下真金白银。芯片一旦投片改一个微架构细节就是一轮新的流片周期动辄几百上千万元。但在架构冻结之前用性能仿真器把软硬件的边界试明白成本几乎为零。后面第4节我会用一个具体例子说明这种提前试错到底怎么操作。2. 算力之外AI芯片里除了乘法器还得有什么2.1 峰值TOPS只是营销数字Roofline模型告诉你真正的天花板先做个简单的算术。假设一颗芯片上有1024个MAC单元乘累加单元频率1GHz做INT8运算那么峰值算力 1024 × 2 × 1G 2TOPS。这个2TOPS是理论上限条件是每个周期每个MAC都在做有效计算。问题来了MAC做一次乘累加至少要消费两个输入A和B还产生一个输出C。假设都是1字节的INT8那么每个周期这1024个MAC就要消耗1024×3字节的数据再乘上1GHz频率每秒需要约3TB的数据带宽。现实里芯片能拿到多少带宽边缘设备用LPDDR4实际可用带宽也就12.8GB/s上下数据中心用HBM能到几百GB/s甚至TB/s。对比一下如果不做数据复用一颗2TOPS的芯片光数据搬运就要3TB/s差距足有两个数量级。所以AI芯片设计的核心问题从来不是堆多少算力而是怎么让每个数据被反复使用把搬运量降下来。这里要介绍Roofline模型这是我们做这类判断的基本工具。它定义了一个叫算术强度的指标每读入一个字节的数据能支撑多少次浮点运算。芯片的峰值算力和峰值带宽之间有一条临界线叫ridge point等于算力除以带宽。某个算子实际的算术强度高于这条线性能卡算力低于这条线性能卡带宽。实际能达到的性能永远是峰值算力和带宽 × 算术强度这两者里比较小的那个。所以设计第一件事就是把目标模型里所有关键算子的算术强度列出来跟自己的ridge point逐项对一遍。2.2 片内SRAM比HBM更金贵显式管理的数据复用设计既然带宽是硬约束片内的数据复用就成了唯一的解药。怎么复用三个手段分块tiling、权重常驻、算子融合。分块是把大矩阵切成能装进片内SRAM的小块让计算在一个块内充分复用权重常驻是让一些模型的固定参数预先留在片上减少重复搬运算子融合则把相邻算子合并避免中间结果落回外部内存再读回来。这些手段本质上都在干同一件事提高算术强度让每字节数据干更多的活。这里有个方向性选择AI芯片到底该不该像CPU那样做统一的硬件cache我的经验和主流做法是一致的大多数AI加速器不搞通用cache而是把SRAM做成多个显式缓冲区比如激活缓冲、权重缓冲由编译器或运行时直接管理配合DMA引擎搬数据。原因是AI计算的访存模式高度规律程序员和编译器可以精确预测用显式管理比做通用cache命中预测省下来的面积和功耗非常可观而且行为可预期不会出现跑某个模型时cache命中率骤降这种玄学问题。代价也很明显软件负担重。编译器要负责决定每个数据块什么时候进片内、什么时候送走、放在哪个bank缓冲区一旦分错性能立刻崩。所以硬件给多少片内SRAM、分成几块、每块多大、DMA通道怎么安排都要从软件角度仔细推敲——这也正是后面软件栈反向定义硬件规格这个命题的源头。2.3 指令集AI芯片的指令到底在指挥谁很多人第一次看AI芯片的指令集都会愣一下这跟CPU指令长得太不一样了。CPU指令是取出一个数、加一、存回去这种粒度AI芯片的指令更像是在指挥一场搬运工程它要说明数据从哪里搬到哪MAC阵列这段周期做什么形状的运算向量单元要不要顺手做个激活。按指令粒度大致分三类各有各的取舍指令粒度灵活性编译器压力适用场景类VLIW低层指令高可精确控制每个执行单元巨大调度难度极高软件团队强大追求极限性能高层算子指令低一条指令表达一次GEMM小软件简洁产品初期优先跑通生态中间路线指令模板中常见算子模式硬编码中多数商用加速器的主流选择不管选哪种都有个共同点指令编码里的每一个位域其实都是软硬件之间的一次约定。比如地址对齐要求、SRAM分区的编号方式、同步事件的传递路径这些东西一旦写进指令集就变成软件永远要去尊重的契约。设计ISA时如果不把编译器能不能高效生成这些指令想清楚后面要么编译器写出歪七扭八的代码要么硬件空有一身性能没人能调度两边都难受。3. 软件栈的三层博弈编译器、运行时与算子库怎么反过来定义芯片规格3.1 图优化与算子融合从来不是纯软件问题先说软件栈反向定义硬件规格这句话怎么理解。AI芯片的软件栈大致三层上层是深度学习框架负责把模型接进来中间是编译器负责把计算图优化成芯片能高效执行的指令序列底层是运行时和算子库负责驱动、内存管理和具体运算。很多人以为这三层只是适配硬件实际上每一层的设计选择都会反过来给硬件提要求。拿算子融合举例。编译器经常做的事是把conv后面的ReLU合并进conv把QK^T之后的softmax前几步合并进来减少中间结果的落盘。但融合要真正省时间硬件必须先提供对应的数据通路MAC阵列算完的结果能不能直接送进向量单元做激活中间不经过外部内存如果片内的运算单元之间没有直连通道编译器就算在图上把算子融了生成的指令还是得把中间数搬出去再搬进来省了个寂寞。再看布局变换。GPU上大家都习惯NCHW或NHWCAI芯片为了访存效率往往希望权重按最内层16个通道连续摆放这样的自定义格式。框架给的是标准排布编译器要在图上插入layout转换算子而layout转换需要在片内留出转置用的临时空间还要多一笔搬运开销。这些都是真实发生的例子软件图优化的清单就是硬件数据通路和SRAM分区设计的需求清单。硬件组的同事如果早点和编译器组把这张清单对齐后期返工能少一大半。3.2 算子库为什么手写kernel永远比自动生成强一截再聊算子库。几乎每颗AI芯片最终都会沉淀出一个手写算子库里面是性能敏感的GEMM、卷积、注意力等高价值算子的手工调优实现。为什么编译器不能自动生成出完美的kernel因为编译器的通用策略很难考虑到微架构的每一层细节DMA什么时候提前发起、数据落在哪个bank能避免冲突、寄存器和SRAM怎么分配才不互相踩脚。这些信息散落在硬件手册的各个角落手工调优能针对具体shape逐个打磨自动生成则只能做到不犯大错性能往往差着一截。所以硬件团队有几个必交付件算子库、性能回归工具和一份性能标杆清单。性能标杆的意思是模型里最常见的几十种shape手册里直接给出参考帧数和最优配置编译器团队和用户在优化时照着对标。这里我亲自吃过亏某算子库初版写得粗糙导致一个大模型的整体性能被拖慢了20%排查了很久才发现是该算子没针对长K、短N的形状做分块片内数据复用率极低。所以算子库不是写出来能用就行要按shape分档调优还要在每次硬件改动后跑回归防止一个微架构调整让某个算子悄悄变慢。3.3 运行时与内存管理多任务并发时带宽配额是设计出来的运行时的戏份也不小。多实例并发跑模型的时候谁来决定DMA带宽先给谁硬件层面多数AI加速器会提供一组带宽配额或优先级寄存器运行时按任务配置这些寄存器实现不同任务间的隔离和保障。如果硬件没有这种机制软件就只能自己排队一旦有个任务贪吃带宽其余所有任务的端到端时延都会雪崩。内存管理也是运行时的大头。前面说过AI芯片普遍不做虚拟内存、不做硬件cache一致性而是要求软件做显式的物理内存规划。运行时在加载模型时会做一次详细的静态规划表把模型的所有权重、中间缓冲区、输入输出槽位排进物理地址空间运行期间基本不再动态分配。这个设计选择的好处是可预测、开销小、不会有缺页异常但代价是灵活性差——遇到动态shape比如输入序列长度变化运行时得准备多套layout或者做大块的内存整理。所以软硬件要事先约定好到底支持哪些动态shape不然运行时写起来会非常痛苦。4. 一个具体的设计决策复盘存储带宽不够时软硬件各自让了哪一步4.1 场景一个边缘端LLM推理加速器项目用我亲身参与的一个模拟项目来做复盘这里就叫X项目吧。目标是在边缘设备上跑一个7B参数级的LLMINT8精度希望达到每秒20个token的生成速度整板功耗控制在5W以内。这个目标不算激进但对芯片设计来说它把三个核心约束同时摆到了桌面上算力、带宽、功耗。立项时我们先做了一件事把目标workload拆开。7B模型推理的decode阶段绝大多数时间花在三大类运算上——QKV投影和MLP这类大GEMM、注意力机制里的QK^T和PV、以及LayerNorm这类元素级运算。我们把每一类的FLOPs、每个权重被读几次、中间结果有多大全部列成一张表。这一步看着不起眼其实是整个项目最值钱的工作因为后面所有软硬件决策都要回到这张表上找依据。4.2 用三个公式判断瓶颈算力、带宽谁在卡脖子判断瓶颈我们只用三个公式。第一个数据搬运下界算子的最小耗时 需要搬的字节数 / 可用带宽这是万事俱备只欠数据时的理想下限。第二个计算耗时 FLOPs / 峰值算力。第三个比较二者——如果搬运耗时远大于计算耗时卡带宽反过来卡算力两者接近才是设计上比较舒服的状态。拿QK^T这个算子来算账单个token解码时查一遍完整KV cache的QK^T本身FLOPs并不大但需要把这条序列的K缓存从头到尾读一遍。序列越长搬运量越大而FLOPs只是线性增长算术强度一路走低。初版硬件配置是2048个MAC、1GHz主频峰值4TOPS片内SRAM只有256KB外部内存用LPDDR4实际带宽约12.8GB/s。用性能仿真器一跑decode阶段的真实利用率只有13%左右瓶颈清晰地指向带宽计算量本身不大但光把数据喂进去的时间就远超计算时间。4.3 软件先让一步融合、量化、连续批处理既然卡带宽第一个反应不是加硬件而是让软件先动手。三件事下去效果立竿见影。第一件是算子融合。把QK^T、softmax、PV在一个指令序列里串起来中间注意力分数矩阵不落外部内存直接在片内完成规约。这一下砍掉的搬运量非常大按序列长度2048算单个head生成的分数矩阵有4M个元素FP16下写一次、读一次就是16MB的往返流量几十个head乘上几十层layer这部分中间落盘要多搬GB级的数据。融合之后这些中间量全部在片上消化搬运量一下就压下来了。第二件是量化。权重从FP16降到INT8KV cache进一步用INT4缓存同样的带宽能支撑翻倍的运算量算术强度立马上来。第三件是连续批处理continuous batching把多个请求的decode合并成一个大batchGEMM的M维度变大权重和KV缓存的读入能被更多token摊薄把算术强度整体抬高了一截。这三步全部是纯软件动作不需要改芯片。做完后再用仿真器跑瓶颈已经从完全搬不过来变成了临界状态。4.4 硬件再让一步削MAC、加SRAM、改流水到了这一步硬件团队重新审视初版配置发现4TOPS其实严重过剩。因为瓶颈在带宽再多的MAC也只能干等着喂数据而堆MAC白白占面积和功耗。于是做了个反直觉的决定把MAC从2048减到1024主频从1GHz降到800MHz峰值算力掉到1.6TOPS。省下来的面积和功耗预算全部投到片内SRAM上从256KB加到768KB同时增加了一路独立的DMA通道让搬下一块权重和算当前block可以真正重叠起来。指令集那边也没闲着为了配合融合后的attention专门加了一条融合指令把软硬件这次约定固定下来。结果很有意思峰值算力下降了2.5倍但在目标模型上实际吞吐从原来预期的十几个token每秒做到稳定20token每秒利用率从13%提升到70%以上。功耗和面积也都达标了还不给散热留包袱。这就是协同设计最典型的收益——不是单纯拼参数而是让软硬件的每一步都打在同一个瓶颈上。配置项初版调整后MAC数量20481024主频1GHz800MHz峰值算力4TOPS1.6TOPS片内SRAM256KB768KB独立DMA通道1路2路实测利用率约13%70%以上目标模型吞吐低于预期稳定20 token/s4.5 复盘为什么削算力反而赢了回头看这个项目能赢的关键是我们在架构冻结之前就把软件分析和性能仿真做完了而不是流片回来再发现问题。很多团队喜欢先堆算力再谈优化结果要么功耗爆炸要么带宽喂不满最后只能靠降频来委屈一下芯片。反过来软硬件一起算账算出来的才是一个真正能落地的方案。还要多提一句这种硬件让一步并不总意味着性能倒退。把面积从过剩的算力里抠出来补给存储和搬运通道相当于把一个大力士但总饿肚子的人变成一个吃得饱的壮汉同一个项目里整体能效反而更好。这是做AI芯片设计最核心的一个取舍逻辑也是我在X项目里收获最大的一课。5. 工程落地中最容易被低估的四个软硬接口5.1 同步与锁步host和芯片等一个信号的代价理论设计再漂亮落到工程里第一个被低估的就是同步机制。host端和加速芯片之间每做一次同步都会产生一次等待等待就是流水线里的气泡。很多AI加速器用任务队列事件通知来异步化host把一堆任务丢给硬件硬件做完一项通知一次host不必每步都停下来等结果。但问题恰恰出在细节上——事件粒度太大host不知道任务内部进度只能在关键节点硬等事件粒度太小芯片光处理通知就忙不过来。这里给出一个实用经验同步点要跟编译器生成的指令流水对齐尽量在图上把可并行的DMA和计算拆开让host侧的等待时间被计算过程吃掉。我见过某项目第一版demo比模拟器慢30%排查到最后原因就是模型里几个小算子之间频繁同步host被绑死在等待上。改完同步策略之后性能立刻回到正常水平。5.2 内存一致性与地址抽象别指望硬件帮你搞定第二个坑是内存一致性。AI加速器大多不提供硬件cache一致性DMA搬完数据之后运算单元能不能看到新数据要靠软件显式地做flush和invalidate。听起来像老式DMA编程确实也是这么回事。所以软件栈里必须有一套统一封装的内存屏障接口不能指望每个kernel作者都去啃硬件手册里的同步细节否则偶发的脏数据bug会让你排查到怀疑人生。地址抽象上也有讲究。芯片若支持虚拟地址页表切换、TLB缺失处理都会带来不确定开销训练和推理框架对此非常敏感。不少AI芯片干脆只在特定场景启用虚拟地址常规路径一律用物理连续内存由运行时提前分配好。作为驱动开发者你要清楚当前路径到底走的是哪套机制不然测试环境和真实环境跑出来的性能会差一大截。5.3 调试与可观测性通电之后你得知道芯片在干什么第三个经验是关于可观测性的。芯片真正通电跑起来之后如果只能看到慢或者错却看不到任何内部状态那优化和排查就是黑盒猜谜。反过来说好的设计会从一开始就带上性能计数器、trace buffer和状态寄存器让软件可以通过profile工具看到每个算子的实际周期数、DMA的空闲率、SRAM的占用峰值、等待事件的时间占比。这些数据可以和simulator的预期逐项对比偏差大就说明有地方没按设计运行。这不是只给性能工程师用的。我在X项目里就吃过没做trace的亏某次模型跑得异常查了两天才通过临时加打印定位到问题——如果硬件本来就有trace几分钟就能找到。所以新芯片立项我会把性能可观测性当成和算力同等级的需求写进规格而不是后补。5.4 流片之后硬件bug要靠软件的workaround来兜底最后一个容易被忽视的现实芯片流片之后几乎总会带一些非致命的小毛病比如某个DMA地址对齐条件触发异常、某种位宽组合下偶发错误。硬件团队不可能每次都为这些小问题重新流片于是软件规避就成了行业常态。做法是在软硬件接口文档里预留errata机制给软件提供可配置的模式或寄存器让驱动在初始化时识别芯片版本按版本启用对应的规避逻辑。我见过比较典型的例子某颗芯片的DMA在突发长度超过一定值时会把最后几个字写乱。硬件不做修改软件这边的workaround是统一约束所有DMA请求的burst长度上限并在分配内存时按固定对齐来切块。性能牺牲很小但完全屏蔽了问题。这个案例说明软硬件接口文档要当成一等公民来写把对齐要求、同步语义、errata规则都写清楚不然等软件踩到问题再去翻设计文档往往已经晚了。先写到这里。做AI芯片的软硬件设计最怕的是两边各算各的账最后流片回来才发现算力很高但模型跑不快。我现在的习惯是任何架构决策出来之前先问一遍编译器能不能把它喂满运行时能不能把它管住再决定投多少面积。系列下一篇我打算聊聊编译器是怎么把一张神经网络图一步步切进那块巴掌大的SRAM里的有兴趣的话可以继续关注。