news 2026/10/12 1:13:00

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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或NHWC,AI芯片为了访存效率,往往希望权重按"最内层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参数级的LLM,INT8精度,希望达到每秒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降到INT8,KV cache进一步用INT4缓存,同样的带宽能支撑翻倍的运算量,算术强度立马上来。第三件是连续批处理(continuous batching),把多个请求的decode合并成一个大batch,GEMM的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每秒,做到稳定20+token每秒,利用率从13%提升到70%以上。功耗和面积也都达标了,还不给散热留包袱。这就是协同设计最典型的收益——不是单纯拼参数,而是让软硬件的每一步都打在同一个瓶颈上。

配置项初版调整后
MAC数量20481024
主频1GHz800MHz
峰值算力4TOPS1.6TOPS
片内SRAM256KB768KB
独立DMA通道1路2路
实测利用率约13%70%以上
目标模型吞吐低于预期稳定20+ token/s

4.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里的,有兴趣的话可以继续关注。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 1:11:31

数据库课程设计实战:健康档案管理系统从需求到SQL实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:11:24

5G NR UCI全面解析:PUCCH/PUSCH承载、格式选型与参数调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:09:31

STM32入门必修课:芯片定位、选型逻辑与开发调试全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:09:29

C-SAM超声扫描声学显微镜检测避坑指南:从换能器选型到图像判读

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:09:26

复杂地形电磁波多径传输建模:基于相对余隙判据的仿真方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:09:25

VMD-SSA组合优化:时间序列分解与预测实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华