我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

AI芯片软硬件协同设计:从算法到硅片的工程实践

AI芯片软硬件协同设计:从算法到硅片的工程实践 1. 从一颗芯片的诞生说起为什么软硬件必须一起设计很多人第一次接触AI芯片脑子里浮现的画面是一块黑乎乎的硅片上面密密麻麻布满了晶体管。但真正做过芯片项目的人都知道一颗AI芯片能不能跑起来、跑得好不好一半靠硬件设计另一半靠软件栈。这两者如果各做各的最后交付的东西基本没法用。我参与过几个边缘推理芯片的项目最深的体会就是AI芯片和传统CPU最大的区别在于它的价值几乎完全由软件定义。一颗CPU你只要保证指令集兼容操作系统和编译器生态会自动帮你把应用跑起来。但AI芯片不一样它的指令集、数据流、内存层次都是为特定计算模式定制的如果软件栈跟不上硬件就是一块昂贵的砖头。这篇文章主要面向三类读者一是刚进入AI芯片领域的软件工程师想搞清楚硬件侧到底在关心什么二是硬件设计人员想理解软件栈对硬件提出了哪些约束三是对芯片设计感兴趣的学生或转行者希望建立一个从算法到硅片的完整认知框架。我会从实际项目经验出发把软硬件协同设计这件事拆开讲透。先给一个最直观的例子。假设你要设计一颗用于目标检测的推理芯片算法团队给过来一个YOLO风格的网络。硬件团队第一反应是算峰值算力多少TOPS、多少MAC单元、片上缓存多大。但真正决定这颗芯片好不好用的是下面这些问题卷积层的算子怎么映射到硬件阵列上特征图在片上和片外之间怎么搬运量化后的精度损失由谁负责补偿这些问题没有一个能只靠硬件或只靠软件单独回答。一个常见的误区是先把硬件架构定下来再让软件团队去适配。这种流程在AI芯片领域几乎必然导致项目延期或性能不达标。2. 算法、编译、硬件三层视角的碰撞与对齐2.1 算法工程师眼中的“计算图”和硬件工程师眼中的“电路”根本不是一回事算法团队习惯用PyTorch或TensorFlow描述模型他们看到的是一个个算子节点组成的有向无环图。在他们眼里一次卷积就是输入特征图和权重做乘加然后加偏置、过激活函数。这个视角是数学的、抽象的不关心数据存在哪里、怎么搬运。硬件工程师看到的则是另一套东西MAC阵列的排布方式、SRAM的带宽和bank划分、总线仲裁策略、时钟树和功耗域。他们关心的是每个周期能完成多少次乘加、数据从DRAM搬到片上的延迟是多少、峰值功耗会不会超过散热设计。这两套语言之间的鸿沟就是编译器和运行时需要填补的。编译器要把计算图切分成硬件能执行的指令序列同时决定哪些数据放在片上、哪些放片外、什么时候预取、什么时候回写。这个过程中每一个决策都会直接影响最终的性能和功耗。我在一个项目里遇到过这样的情况算法团队用了一个带分组卷积的轻量网络参数量很小理论算力需求不高。但硬件团队设计的MAC阵列是固定尺寸的分组卷积导致每个分组的通道数无法填满阵列实际利用率只有理论峰值的30%左右。最后解决办法是在编译器层面做算子融合和通道重排把分组卷积转换成等效的普通卷积来跑。这个改动如果等到流片之后再发现代价就是几百万的掩膜费用。2.2 量化不是“压缩一下”那么简单量化是AI芯片绕不开的话题。把FP32的权重和激活值降到INT8甚至INT4可以大幅减少内存带宽需求和计算单元面积。但量化对精度的影响不是线性的不同层对量化的敏感度差异很大。我通常建议的做法是先做逐层敏感度分析再决定混合精度策略。具体来说用校准数据集跑一遍推理统计每一层激活值的动态范围然后对敏感层保留较高精度对不敏感的层大胆降精度。这个过程需要算法、编译、硬件三方一起看数据。硬件侧要支持混合精度意味着MAC单元要能灵活配置位宽片上缓存要能处理不同精度的数据排布。软件侧则要在编译器中实现精度传播和算子选择逻辑。如果硬件只支持单一精度软件再怎么优化也突破不了天花板。2.3 内存墙才是真正的瓶颈很多AI芯片标称算力很高但实际跑模型时利用率很低根本原因往往出在内存带宽上。以一次标准的卷积计算为例假设输入特征图是H×W×C卷积核是K×K×C×F那么每完成一次输出像素的计算需要读取K×K×C个输入数据和对应的权重。如果这些数据都从DRAM读取带宽需求会非常恐怖。解决思路无非几种提高片上缓存容量、优化数据复用策略、减少数据搬运次数。但每一种都有代价。片上缓存大了芯片面积和成本上去了数据复用策略复杂了编译器的调度难度指数级上升。我在实际项目中的经验是在架构设计早期就把典型模型的访存模式分析清楚用仿真工具跑一遍数据流看看片上缓存放不放得下关键层的中间结果。如果放不下就要考虑在编译器层面做分块计算tiling把大特征图切成小块逐块处理。分块的大小和顺序会直接影响缓存命中率这需要编译器和硬件协同设计。3. 指令集与数据流AI芯片架构设计的核心抉择3.1 指令集的粒度决定了编程的灵活性AI芯片的指令集设计是一个权衡游戏。指令粒度太粗比如一条指令完成整个卷积层编程灵活性差遇到新算子就得改硬件指令粒度太细比如一条指令只完成一次乘加编译器要生成的指令数量爆炸取指和译码的功耗占比会高得离谱。目前主流做法是采用中等粒度的指令比如一条指令完成一个向量运算或一个小矩阵运算。这样既保留了灵活性又控制了指令数量。但具体怎么划分要看目标模型的计算模式。我参与过的一个项目采用了“宏指令微指令”的两级方案宏指令描述一个算子的整体行为微指令描述具体的计算步骤。编译器先把计算图映射成宏指令序列再由硬件调度器展开成微指令执行。这种方案的好处是编译器不用关心底层细节硬件也能灵活调度。坏处是硬件调度器的设计复杂度很高验证工作量很大。3.2 数据流架构的选择脉动阵列 vs 流式处理脉动阵列是AI芯片里很常见的一种数据流架构它的核心思想是让数据在计算单元之间流动减少对内存的访问。每个计算单元只和相邻单元通信数据像波浪一样在阵列中传播。这种架构非常适合卷积运算因为卷积的权重可以预先加载到阵列中输入数据流过阵列就能得到输出。但脉动阵列也有局限性。它对矩阵乘法的形状很敏感如果矩阵维度不匹配阵列利用率会下降。而且脉动阵列的编程模型相对固定遇到非卷积类的算子比如注意力机制里的softmax就不太适用。另一种思路是流式处理架构数据像流水线一样经过各个处理阶段。这种架构更灵活可以支持多种算子但数据复用率不如脉动阵列高对内存带宽的要求更大。实际项目中很多芯片会采用混合方案卷积类算子用脉动阵列加速其他算子用通用处理单元。编译器负责根据算子类型选择执行单元。这种异构架构的调度策略是软件栈里最复杂的部分之一。3.3 片上网络的带宽分配问题当芯片上有多个计算单元和多个存储单元时它们之间的通信就变成了一个网络问题。片上网络NoC的设计直接影响数据搬运的效率和功耗。一个常见的坑是计算单元之间的带宽分配不均匀导致某些单元成为瓶颈。比如在Transformer类模型中注意力计算需要频繁地在不同位置之间交换数据如果NoC的拓扑结构不适合这种通信模式性能会大打折扣。我的经验是在架构设计阶段就用流量模型仿真NoC的负载情况看看最坏情况下的延迟和带宽是否满足要求。如果发现瓶颈可以考虑调整拓扑结构、增加路由节点、或者优化数据排布来减少通信量。4. 软件栈的每一层都在为硬件“翻译”4.1 图编译器从计算图到硬件指令图编译器是AI芯片软件栈的核心组件。它的输入是框架导出的计算图输出是硬件能执行的指令序列。这个过程大致分为几个阶段图优化、算子融合、内存分配、指令生成。图优化包括常量折叠、死代码消除、算子替换等。算子融合是把多个小算子合并成一个大算子减少中间结果的读写。比如把卷积、批归一化、激活函数融合成一个算子可以显著减少内存访问。内存分配是编译器里最考验功力的部分。片上缓存有限哪些张量放在片上、哪些放片外、什么时候换入换出这些决策直接影响性能。我见过一些编译器采用贪心策略先分配大的张量再分配小的。但这种方法在复杂模型上效果不好因为张量的生命周期有重叠贪心策略容易导致碎片化。更好的做法是基于生命周期分析做内存规划把生命周期不重叠的张量分配到同一块内存区域。这需要编译器对计算图有全局视野不能只看局部。4.2 运行时调度动态决策的艺术编译器生成的是静态指令序列但实际运行时的情况可能和编译时假设的不一样。比如输入数据的形状可能变化、某些层的计算时间可能因为缓存命中率而波动。运行时调度器需要根据实际情况动态调整执行顺序和资源分配。一个典型的场景是多模型并发执行。比如自动驾驶芯片上同时跑感知、规划、控制多个模型它们对延迟的要求不同。运行时调度器需要根据优先级和资源占用情况动态分配计算资源。我在项目里实现过一个基于时间片轮转的调度器每个模型分配一个时间片超时就让出资源。但这种方法对延迟敏感的模型不友好后来改成了优先级抢占式调度高优先级模型可以打断低优先级模型的执行。代价是上下文切换的开销增加了需要硬件支持快速的上下文保存和恢复。4.3 算子库手写优化的最后一道防线虽然编译器能自动生成大部分算子的实现但有些关键算子还是需要手写优化。比如卷积、矩阵乘法、注意力计算这些核心算子手写实现往往能比编译器自动生成的高出20%到50%的性能。手写算子的难点在于要充分利用硬件的特性MAC阵列的排布、寄存器的分配、流水线的调度。这需要工程师对硬件架构有深入理解。我通常的做法是先用编译器生成一版基线实现然后用性能分析工具找出瓶颈针对性地手写优化。手写算子的维护成本很高硬件架构一变就得重写。所以我的建议是只对最核心、最影响性能的算子做手写优化其他算子交给编译器。同时要建立完善的回归测试确保手写算子在各种输入形状下都能正确运行。5. 那些只有做过芯片的人才知道的坑5.1 仿真和流片之间的鸿沟在流片之前所有验证都是基于仿真和FPGA原型。仿真环境下的性能和功耗数据和实际芯片之间往往有差距。这个差距可能来自工艺偏差、寄生参数、温度影响等多个方面。我经历过最惨的一次是仿真环境下功耗完全达标流片回来之后实测功耗超了30%。排查了很久才发现是时钟树的功耗被低估了仿真模型里没有准确反映时钟缓冲器的实际功耗。这个问题在架构设计阶段根本看不出来只有流片之后才会暴露。所以我的建议是在架构设计阶段就留足功耗和面积的余量不要卡着指标设计。同时要建立从仿真到实测的校准流程用实测数据反过来修正仿真模型为下一个项目积累经验。5.2 编译器开发和硬件开发的节奏错位硬件开发周期长从架构设计到流片通常需要一年以上。编译器开发周期相对短但依赖硬件架构的稳定性。如果硬件架构频繁变动编译器团队就得反复修改效率很低。解决这个问题的关键是尽早冻结硬件架构的关键接口比如指令集编码、寄存器映射、内存布局。这些接口一旦确定编译器和硬件就可以并行开发。架构的微调可以在仿真阶段通过参数配置来验证不影响接口定义。我在项目里推行过一个做法硬件团队和编译器团队每周开一次同步会硬件团队同步架构变更编译器团队反馈软件侧的约束。这个机制听起来简单但执行起来需要双方都有很强的协作意识。5.3 精度问题的排查链路AI芯片的精度问题往往很难排查因为误差可能来自量化、近似计算、累加顺序等多个环节。我总结了一套排查流程在实际项目中比较有效。第一步是逐层对比。用同一组输入数据分别在GPU和AI芯片上跑推理逐层对比输出。如果某一层开始出现明显偏差就锁定问题层。第二步是算子级复现。把问题层单独拿出来用相同的输入在芯片上跑和参考实现对比。如果偏差仍然存在说明是算子实现的问题如果偏差消失说明是层间交互的问题。第三步是数值分析。检查量化参数是否合理、累加顺序是否导致精度损失、是否有溢出或下溢。这一步往往需要算法和硬件工程师一起分析。精度问题最忌讳的是“大概差不多就行”的心态。在AI芯片领域1%的精度损失可能导致整个模型不可用。5.4 散热和功耗的隐性约束AI芯片的功耗密度很高散热设计如果不合理芯片会降频甚至宕机。我在一个边缘设备项目里遇到过这样的情况芯片在实验室环境下跑得好好的装到设备里之后因为散热空间有限温度很快升到阈值性能直接砍半。解决散热问题不能只靠加散热片要从架构设计阶段就考虑功耗分布。比如把高功耗的计算单元分散布置避免热点集中采用动态电压频率调整DVFS根据负载实时调节功耗。软件侧也可以配合做功耗优化。比如在模型推理的间隙插入空闲周期让芯片有时间散热或者根据温度传感器的读数动态调整推理批次大小。这些策略需要软硬件协同实现。6. 从项目实践中提炼的协同设计方法论6.1 建立统一的性能模型软硬件团队如果各用各的性能模型沟通起来会非常困难。硬件团队用周期精确的仿真器软件团队用框架级的性能分析工具两者的数据对不上讨论就没有基础。我的做法是建立一个中间层的性能模型抽象掉具体的硬件实现细节只保留对软件有影响的参数峰值算力、内存带宽、缓存容量、指令延迟。软件团队用这个模型做快速评估硬件团队用详细仿真验证关键场景。两边的数据定期对齐确保模型准确。这个性能模型还有一个好处可以用来做设计空间探索。比如调整缓存容量或带宽参数看看对典型模型的性能影响有多大帮助硬件团队做架构决策。6.2 用典型模型驱动架构定义AI芯片的架构不应该拍脑袋决定而应该由目标应用场景的典型模型驱动。在项目启动阶段我会收集一批有代表性的模型覆盖不同的算子类型、不同的计算模式、不同的精度要求。然后用这些模型做架构仿真看看什么样的架构能最好地支持它们。这个过程需要算法团队的配合因为他们最清楚未来一到两年内模型会往什么方向演进。如果架构只支持当前的模型等芯片量产时可能已经过时了。我在项目里通常会留出20%到30%的架构余量用于支持未来可能出现的新算子。这部分余量可能暂时用不上但能延长芯片的生命周期。6.3 迭代式开发与快速验证AI芯片的开发周期很长如果等到所有模块都完成再集成风险会非常大。我倾向于采用迭代式开发先做一个最小可用的版本跑通端到端的流程然后再逐步优化。比如第一版可以只支持一种算子、一种精度验证从模型导入到芯片执行的完整链路。第二版增加算子类型和精度支持。第三版优化性能和功耗。每一版都有明确的验证目标避免了一次性集成带来的巨大风险。快速验证的另一个关键是建立自动化的回归测试体系。每次代码提交都自动跑一遍典型模型对比输出精度和性能指标。一旦发现回归立即定位和修复。这个体系在项目后期尤其重要因为那时候改动频繁人工验证根本忙不过来。6.4 文档和接口规范的重要性软硬件协同设计涉及多个团队的协作接口规范如果不清晰沟通成本会非常高。我在项目里坚持的一个原则是所有跨团队的接口都必须有文档化的规范包括指令集编码、寄存器映射、内存布局、数据格式等。这些规范不是写完就完了还要随着项目进展持续更新。我见过太多项目因为接口文档过时导致集成失败。所以我会指定专人负责维护接口规范任何变更都要经过评审和通知。文档的另一个作用是沉淀知识。芯片项目的周期长、人员流动大如果没有好的文档新人上手会非常困难。我通常会要求每个模块的负责人写设计文档包括设计思路、关键决策、已知问题等。这些文档在项目后期排查问题时非常有用。7. 一些实际项目中的经验碎片做AI芯片这几年踩过的坑和积累的经验远不止上面这些。再分享几个零散但很实用的点。关于时钟频率的选择不要盲目追求高频率。频率越高功耗越大时序收敛越难。我见过一些项目为了追求标称算力把频率拉得很高结果实际运行时因为功耗和散热限制根本跑不到那个频率。比较务实的做法是选择一个功耗和性能平衡的频率点通过架构优化来提升算力而不是靠拉频率。关于验证策略AI芯片的验证工作量非常大因为要覆盖各种模型、各种输入形状、各种精度配置。我的经验是用随机测试定向测试结合的方式。随机测试用大量随机生成的模型和输入来发现边界情况定向测试针对已知的易错场景做深入验证。两者结合能在有限的时间内达到较好的覆盖率。关于与算法团队的协作算法团队和芯片团队的语言差异很大沟通时容易鸡同鸭讲。我的做法是让算法团队尽早参与硬件架构的评审让他们从模型的角度提意见。同时让芯片团队的人去跑一跑主流框架的模型亲身体验一下算法工程师的工作流程。这种换位思考能显著减少沟通障碍。关于流片前的检查清单流片是芯片项目里风险最高的环节一旦出错代价巨大。我通常会准备一份详细的检查清单覆盖功能验证、时序收敛、功耗分析、DFT、封装等各个方面。每个检查项都有明确的通过标准和负责人。这份清单在流片前要反复过几遍确保没有遗漏。关于软件栈的向后兼容芯片可能会出多个版本软件栈要能兼容不同版本的硬件。这要求在软件设计时就考虑硬件抽象层的设计把硬件相关的部分隔离出来用统一的接口对上层的编译器和运行时。这样新硬件出来时只需要更新硬件抽象层上层的软件不用大改。最后说一点关于团队建设的体会。AI芯片的软硬件协同设计需要复合型人才既懂算法又懂硬件还懂编译器的人非常稀缺。我的做法是在团队内部建立知识分享机制让硬件工程师给软件团队讲架构让算法工程师给硬件团队讲模型。同时鼓励大家跨领域学习不要把自己局限在单一角色里。这种团队氛围建立起来之后协作效率会有明显提升。软硬件协同设计不是一句口号而是需要落实到每一个设计决策、每一次团队沟通、每一行代码里的具体实践。
返回列表