首页 / 资讯中心 / 文章详情

AI芯片软硬件设计核心:从架构定义到编译器优化的实战经验

AI芯片软硬件设计核心:从架构定义到编译器优化的实战经验 ★ FEATURED ARTICLE
AI芯片这两年热度一直不减但要真正把一颗芯片做成能用、好用、卖得出去的产品光靠堆算力远远不够软硬件设计才是真正的深水区。我在这行摸爬滚打了几年经历过从架构定义到流片回归再到软件栈跑通的全过程踩过不少坑也积累了一些实实在在的心得。这篇东西不是教科书更像是一份工程实践笔记把AI芯片软硬件设计的核心逻辑、关键环节和那些文档里不会写的经验教训掰开揉碎讲清楚。不管你是刚入门的学生、想转行的软件工程师还是已经在做相关项目的开发者应该都能从这里找到点有用的东西。1. AI芯片设计的起手式先定软硬件边界很多团队做AI芯片一上来就陷入细节这里加个算子、那里做个加速器、内存带宽要多大、编译器的指令怎么安排。结果往往是硬件做出来了软件跑不动或者软件设计得很美好硬件根本实现不了。我个人的经验是AI芯片设计的第一个关键动作不是画框图而是想清楚软硬件边界划在哪里。1.1 为什么说软件定义硬件AI芯片和传统芯片最大的不同在于它的“用户”不是人而是算法框架和编译器。你用TensorFlow还是PyTorch模型是卷积多还是Transformer多训练还是推理这些直接决定了硬件该长什么样。换个角度说你设计的不只是一块芯片而是一个能高效执行一组特定计算模式的执行引擎。这个思路在行业里叫“软件定义硬件”听起来玄乎其实核心就一句话先搞清楚你要跑的算法是什么样的数据流和计算模式再决定硬件提供什么样的原语和抽象。拿我参与过的某AI加速芯片项目举例。项目初期架构组花了两周时间做了大量的算法负载分析。我们把当时主流的视觉模型、语音模型、NLP模型全部梳理了一遍统计出几个关键数据矩阵乘法的尺寸分布、卷积的stride和padding模式、激活函数的种类、量化精度INT8还是FP16、张量的shape变化规律。这些统计结果直接决定了我们后来硬件上放几个矩阵计算单元、每个单元多大的MAC阵列、片上SRAM分配多少给权重多少给激活值。如果你跳过了这一步架构师凭感觉拍脑袋定了规格后面软件团队接手时会发现处处掣肘。算子张量形状奇奇怪怪编译器没法高效映射内存访问模式太随机DMA引擎根本忙不过来。到头来芯片算力是挺高实际利用率可能连20%都不到。1.2 一套规格书的诞生过程软硬件边界最终要落在一份“指令集架构”或者“硬件抽象层”的规格书里。这份文档定义了软件能“看到”的硬件是什么样子也决定了硬件实现的自由度。我们在做这个阶段时有几条原则特别重要原语要少而精硬件暴露给软件的原语指令/操作越少越好。每个额外功能都意味着验证成本、硅片面积和软件适配难度。一个操作如果可以通过组合已有的几个原语实现就不要做专门的硬件指令。类型要完整覆盖主流场景比如你要支持INT8就不能只支持矩阵乘法的INT8还要支持向量运算的INT8、累加器位宽的INT8。否则编译器遇到某个算子就会“巧妇难为无米之炊”。语义要简单明确硬件的每条指令行为必须无歧义。最怕的是那种“视情况而定”的描述硬件工程师和编译器工程师理解不一致后期联调就是灾难。我们在某次架构评审时就因为一个“可选支持”的功能吵了一周。编译器团队说这个功能很重要没有它某些模型性能会掉一半硬件团队说这个功能太复杂时序收敛不了面积也超标。最后两边的架构负责人坐下来拿着最新的算法趋势数据重新过了一遍砍掉了那个功能把省下来的资源翻倍投资到另一个更核心的计算单元上。事后证明这个决定是对的因为后来主流算法确实没用到那个被砍的功能。2. 硬件架构的核心抉择算力、带宽与数据流软件边界定了硬件架构师就有活干了。这个阶段的关键词是算力、带宽、数据流、存储层次。很多人觉得AI芯片就是堆MAC乘累加单元数字越大越厉害。但实际上峰值算力和实际性能之间隔着一条巨大的“效率鸿沟”。2.1 计算单元从SIMD到脉动阵列AI计算的核心是矩阵乘法和卷积。对应的硬件加速结构主要有几种流派。早期GPU用的是SIMT/SIMD的思路大量较小的处理单元并行执行灵活但效率一般。专用AI芯片往往采用更结构化的阵列比如脉动阵列。脉动阵列的理念很有意思数据像心跳一样在PE处理单元间有节奏地“流动”每个PE只做本地计算数据从相邻PE传入再传向另一个邻居。这样设计有几个好处数据复用率高不需要反复从全局存储中取数布线短频率容易拉高功耗低因为数据搬运少了。但这东西不是没有代价的。脉动阵列对数据流形状有要求一旦矩阵尺寸和阵列尺寸不匹配就会有大量空转。我们设计时留了多个数据流模式权重固定weight-stationary、输入固定input-stationary、输出固定output-stationary编译器根据具体算子选择匹配的模式这一块后面软件章节会细说。另外一个经常被忽视的细节是累加精度。矩阵乘法的中间结果如果只累加在INT32甚至INT16里在大矩阵、大Channel的场景下很容易溢出或者损失精度。我们硬件上给每个PE内部做成了INT32累加器部分算子还支持FP32累加喂给软件接口需要bit-exact的精度行为定义。这个细节会在算法精度对齐时反复被提及。2.2 存储墙与片上存储设计AI芯片性能杀手第一是存储带宽第二还是存储带宽。算力再高数据喂不进来就是白搭。行业里叫“存储墙”memory wall这是所有做AI芯片的人绕不过去的坎。我们当时的做法是构建多层存储体系。片上主要有三块全局SRAM或者叫紧耦合内存TCM、分布式SRAM分布在各个PE附近、还有寄存器级缓存。片外是DDR或者HBM。设计的关键决策是每个数据元素在片上被重复利用多少次才能让有效带宽等于你期望的外部带宽乘以复用系数。举个例子假设我们的MAC阵列峰值算力是100 TOPS 1GHz也就是说每秒需要200 Tera个操作乘和加各算一个。如果每个权重只被使用一次那外部存储带宽至少需要200 TB/s。HBM2E单颗大概460 GB/s需要400多颗谁能承受得了所以权重和激活必须在片上被大量复用把外部带宽的实际需求降下来。我们当时的方案是每个PE内部放了一小块Register File缓存权重分片同一份权重流式地被多个输入数据重用激活值走另一条路径在多个输出通道间共享。这样实际需要的外部带宽降到了几TB/s的量级。设计时我们反复调整片上SRAM的容量分配权重缓存更多还是激活缓存更多配合编译器做buffer规划每次调整都要通过片上网络NoC的仿真验证。这里有个工程上不起眼但特别关键的教训片上SRAM的bank数量和组织方式。如果bank数少不同访问请求会冲突带宽利用率直线下降如果bank数多面积和功耗又上去。我们最后针对矩阵访问的模式做了bank切分让权重和激活的访问尽量落在不同bank上有效命中率明显提升。2.3 互联与数据搬运的账本多核AI芯片还要考虑核间互联。数据从一个计算核搬到另一个计算核走片上网络路径长短和拥塞程度直接决定效率。从软件角度你希望“所有地址都是平等的”从硬件角度离得远的两个核通信就是贵。我们为tensor搬运设计了一套DMA引擎支持二维/三维的地址跳转这样编译器可以直接把张量的view或者transpose操作映射为DMA描述符不需要CPU逐个元素去搬。这个DMA控制器本身也是软件栈的重要一部分编译器要懂得怎么生成最高效的搬运序列避免小包搬运带来的利用率下降。互联拓扑我们最终选了简单的二维Mesh加广播优化。为什么不用更复杂的环形或者全互联因为在当时的负载下点对点通信和广播通信已经很够用了而Mesh在物理实现上最规整、时序最好收敛。拓扑的选择没有绝对正确答案只有“适合你的负载模型”的答案。3. 软件栈的硬骨头编译器与算子库硬件做得再漂亮软件栈不行芯片就是一块昂贵的砖头。AI芯片的软件栈通常包括驱动、运行时runtime、算子库、编译器、调试分析工具。其中编译器是最核心也是最难的。3.1 算子映射把算子压进硬件里每个AI框架里的算子最终都要落到硬件指令上。编译器要做的第一件事是把计算图里的算子Conv、MatMul、Add、LayerNorm等分解成硬件支持的多个阶段布局转换、数据搬运、矩阵计算、向量计算、激活/归一化、输出写回。这听起来像一个纯软件问题其实每个阶段都嵌着硬件的约束条件。比如布局转换NHWC还是NCHW向量算子喜欢NHWC通道维在最后访存连续但矩阵单元可能更喜欢NCHW通道维在中间利于batch复用。编译器需要插入layout转换节点这个转换本身要花时间所以有时宁可让矩阵单元空一点也不频繁转布局。数据搬运片上SRAM放不下大tensor时要分块tile。tile的大小必须对齐DMA的burst长度和bank的访问粒度否则带宽使用效率就会很差。指令调度矩阵单元、向量单元、DMA单元可以并行工作编译器要生成一个类似流水线的调度让数据流一直处于“满水”状态。我见过很多次软件团队想当然地写了一个“通用”映射策略结果在特定模型上比手工汇编差5倍。后来我们干脆为每个高频算子写了一套优化模板在模板框架内做参数搜索和调优。这套算子库和编译器的协同是日常性能优化的重要工作。3.2 编译器优化与调度编译器的中间表示IR也不能照搬LLVM那套。AI芯片编译器需要的是面向张量的IR同时要能表达张量的分块、数据依赖和硬件资源的占用情况。我们的编译器有两层IR上层是计算图级的IR做算子融合、内存分配、布局选择下层是指令级的IR做循环展开、双缓冲double buffering、并行度分配。算子融合是个很经典也很有用的优化。比如“ConvBNReLU”如果拆成一个一个算子做每个算子都要从片上读完再写回来回搬运浪费了时间和带宽。融合之后BN的参数预先折算到卷积的权重和偏置里ReLU在计算单元读取结果时同步完成数据全程在片上流动省了一整个tensor来回。双缓冲技术则是解决DMA搬运和计算重叠的问题。我们简单一点理解人为地把计算和搬运切分成两个阶段计算单元在处理第一块的搬运结果时DMA已经在搬运第二块了。这样需要两块scratch buffer。在资源有限的片上怎么切分buffer大小、怎么安排调度顺序是一个需要精细调节的优化点。我们编译器里有个开关分几种buffer分配策略默认一种保守策略性能开发者可以根据具体模型手动调整。很多AI芯片设计者低估了调试器的重要性。当模型性能不达标时你打开profiler看到一堆空白和停顿如果没有足够细粒度的硬件事件计数cycle count、stall reason、DMA占用率你根本不知道瓶颈在哪里。所以我们花了大量的精力在硬件性能计数器的设计上让每一类stall都有原因可查。这部分开始可能觉得“浪费性能”一旦到了调优阶段你会谢天谢地当初做了这些。3.3 调试工具链的搭建调试工具链是软件栈里最容易凑合、也最影响效率的环节。我见过有些团队用日志加内存dump的做法调驱动和算子一个bug定位一整天。后来我们架设了三个层次的debug方案仿真器emulator硬件还没回来之前用软件模拟器跑完整栈的功能验证。这一层最便宜、最灵活适合写算子逻辑和找debug逻辑错误。FPGA原型验证把核心逻辑烧到FPGA上跑真实框架模型。速度比仿真快几个数量级但能跑的规模有限。这一层主要验证硬件和软件接口的配合以及编译器生成的指令流在真实时序下的行为。芯片级调试芯片回来后通过JTAG或者片上debug模块做寄存器级dump和指令trace。这一层最难但也最接近真实覆盖场景。我们当时在这套工具链上的投入大概占了整个软件团队人力的三分之一看似奢侈但实际省下了后面大量的联调时间。4. 软硬件协同的实战记录与常见问题做AI芯片分工是为了提效但真正让产品变得可用的是“协同”。协同意味着硬件团队要理解软件的需求软件团队也要知道硬件的脾气。下面分享几个我们在实际项目里遇到的问题和解决过程都是些不写在论文里的经验。4.1 一次性能调优的真实案例有段时间我们的加速器在跑某个Transformer模型时MLP部分效率特别低MFU模型利用率只有40%。用profiler一查发现DMA和计算之间有大量空泡计算单元在等数据。排查后发现DMA的搬运粒度设得太小了。编译器为每个tile生成独立的搬运指令每个指令都要付出固定开销地址计算、通道握手等小tile意味着这些开销变成瓶颈。解决办法有两步。第一在编译器里把多个相邻tile合并成一个大的搬运块降低指令数量第二为MLP里的矩阵计算临时加大tile尺寸让每块数据在片上被更充分地重用把计算和搬运的比例拉回来。调整后MFU提升到了75%以上整体性能翻倍。这个案例最好的部分在于不是硬件哪里坏了也不是软件哪里写错了而是两者之间缺少“匹配”。如果硬件团队当初没有预留足够的片上buffer空间编译器再怎么做tile合并也巧妇难为无米之炊。软硬件设计从来都是一盘棋。4.2 板级验证阶段的问题排查速查表芯片回来后问题就换了一种属性。功能仿真跑得再好真实物理世界总有奇怪的现象。我们整理了一份常见问题、现象和排查思路的速查表因为这类问题每颗芯片都躲不掉现象可能原因排查思路高负载下随机出现错误结果时序违例或电压降降低频率验证检查电源完整性模型用循环冗余校验定位失败模块特定算子总是算错累加器精度溢出或量化处理不对对比仿真器逐层输出检查累加器位宽设置和溢出处理策略DMA传输偶尔丢数据地址未对齐到burst边界检查描述符地址对齐检查bank冲突和仲裁逻辑片上网络在高拥塞时性能骤降拥塞控制策略过于简单测试不同路由策略统计各节点访问延迟热点图编译器生成的指令序列在芯片上跑得特别慢指令缓存/解码器瓶颈统计指令fetch效率检查是否存在大量重复的指令片段这张表看着简单却是我们花了无数个深夜debug才换来的。每一类问题背后都有对应的硬件设计和软件配套逻辑排查时必须软硬结合单看任何一边都可能绕圈。4.3 给后来者的实操建议如果只让我给做AI芯片软硬件设计的团队提三条建议我会说尽早做端到端的负载验证不要在硬件都定型了才把真实模型拿过来。哪怕是用简化版的模拟器/FPGA也让编译器团队尽快把真实模型的算子跑起来越早暴露问题改架构的成本越低。软件栈要跟硬件一起演进硬件架构师出到第三版规格时编译器团队必须有第一版的完整实现而不是等硬件冻结了才动手。软硬件并行开发的基本原则是“接口先行”指令集和硬件抽象层一旦冻结双方就可以各干各的了。性能分析工具必须从第一天就设计等你需要它的时候再设计就晚了。硬件性能计数器的寄存器定义、profiler的数据格式、trace信息的落点这些都要在架构阶段就和指令集一起定义。做AI芯片软硬件设计最忌就是两面各做各的最后拿到一起才发现“我们以为你说的是A你说的是B”。软硬件协同的本质不是一个阶段而贯穿始终的工作方法。每隔一段时间架构、硬件、软件三方要坐在一起重新对一遍当前的限制、新发现的问题、策略的变更让信息流动起来。后来我还参与过另一个项目一开始就定了一条规矩任何影响硬件接口的改动哪怕只是增加一个状态位都必须经过软硬件双方负责人共同签字。听着很官僚实际上杜绝了大量“想当然”的问题。工程很多时候不考验谁更聪明而考验谁更少犯自以为是的错误。5. 最后说点个人体会做AI芯片这一行很多人盯着峰值算力、制程工艺、HBM带宽这些指标真正决定产品成败的往往是设计团队对“软硬件作为一体”这个理念的执行深度。硬件定下来的规格软件能不能高效吃透软件提出的需求硬件愿不愿意在面积和时序上买单这种博弈与折中每天都在发生。我自己的感受是在这个领域成长最快的方式不是多看论文而是从头到尾跟一个项目走一遍从算法负载分析到架构定义从RTL验证到编译器开发再到FPGA原型和芯片调试。每个阶段都会刷新一次你对“软硬件协同”的理解。中间一定会有痛苦和反复但当你看到自己参与设计的芯片跑通了第一个真实模型那种成就感是其他工作很难替代的。如果你正准备入坑建议先别急着选边站——不要一开始就说“我只做硬件”或者“我只做软件”。试着去理解对面那帮人的思维方式和约束条件慢慢你会发现优秀的架构师、优秀的编译器工程师都是跨领域的思考者。AI芯片的软硬件设计说到底是一门平衡的艺术。
阅读完成 · 觉得有帮助?
咨询建站