说实话,真正开始做AI芯片项目之前,我一直觉得这块硬件的核心是堆算力——Flops够高,性能就一定好看。直到自己上手写了好几个月的NPU驱动和编译器,又跟着调了好几个月的板子,才明白一个反直觉的道理:AI芯片设计里最难的那道题,从来都不是计算单元,而是数据怎么流动。
这篇是这个系列专栏的第六篇。前面几篇分别聊了AI芯片的架构演进、存储子系统、编程模型和工具链基础,这一篇想把“软硬件协同设计”这件事完整串一遍。所谓软硬件协同设计,简单说就是:硬件的能力边界是在设计阶段由软件需求倒推出来的,而不是流片之后再靠软件去修补。围绕这个主题,我会从算力与带宽的矛盾、NPU规格定义、指令集与算子映射、流片前的协同验证、板级调试,以及未来面向AI Agent负载的接口演进这几个角度展开。
这篇内容可能更适合正在做嵌入式AI部署、芯片验证或编译器开发的人。哪怕你只是做应用层,看完也会理解为什么同一套模型在不同设备上表现天差地别——那不是手机不行,而是数据搬运这件事被想简单了。
1. 一个根本矛盾:算力上去了,数据还在“堵车”
1.1 用Gemma-2B算一笔访存账
先别急着看架构图,我们来算一笔最简单的账。假设要在某个端侧芯片上跑一个20亿参数的大语言模型,精度是FP16,那么模型本身的权重就有4GB数据。如果要求端侧推理延迟控制在200毫秒以内,光是把这4GB权重从DDR读进计算单元一次,就需要至少20GB/s的有效带宽。注意,这还只是权重的读取,还没算激活值、KV Cache和中间张量的读写。
很多芯片标称几十万亿次每秒的AI算力,但实际推理时远远跑不满。原因就在这:数据从内存走到计算单元的速度,决定了你到底能用上多少算力。我接触过不少做AI应用的朋友,他们总觉得模型推理慢就是算力不够,实际上拿profiler一抓,计算单元的利用率往往只有百分之十几,剩下的时间全部耗在内存访问和同步等待上。
这个问题的本质,是当前大部分通用处理器的存储层级,本来就是按“尽可能降低访问延迟”设计的,而不是按“尽可能提升可持续带宽”设计的。AI计算恰恰相反,它更接近流式计算——数据非常规整地流过计算阵列,最怕的就是某个时刻某个数据还没准备好,整个流水线停下来干等。
1.2 三种架构路线凭什么活下来
图上画得再复杂,业界解决这个问题的思路也就三条线。
第一条是堆带宽,典型代表就是给HBM或者LPDDR5X这种超宽内存接口,再配上大容量的片上SRAM。英伟达的GPU在很长一段时间里赢得不费劲,靠的不是计算单元比谁多,而是它的显存带宽和互联矩阵确实把对手甩开几条街。
第二条是减少搬运次数,也就是所谓的“近存计算”或者“存内计算”。把计算单元贴着存储单元放,让数据少走一段路。这种思路在学术界炒了很多年,但真正工程落地的还是少数,主要原因是半导体工艺上做存储和做逻辑的器件工艺很难完全兼得。
第三条也是目前端侧NPU最常用的一条,是“数据流控制”——把计算分成一层一层的小流水,用软件编译器在上层做好调度,让数据像车间里的传送带一样平稳地流过每一个计算单元。不需要超高的瞬时峰值带宽,但能保证数据每个周期都在移动,计算单元永远有活干。
这三条路线不是互斥的,现实中往往混合使用。而无论走哪条,最终都要求软件层能意识到硬件的物理限制。这才是软硬件协同设计的第一个核心法则:硬件决定了性能的理论上限,软件决定你实际能拿到多少。
1.3 所以软件设计的出发点是“数据流”
很多第一次做AI芯片的人容易犯一个错误:硬件RTL写得很漂亮,计算阵列规模也够大,但软件侧完全按CPU的思路去写驱动和推理代码——一个算子一个算子的调用,每次调用都把数据从DDR拉到SRAM,算完再写回去。结果硬件理论算力10TOPS,实际跑起来连1TOPS都到不了。
实际做的时候,一个好的软件栈往往从“数据流”开始设计。先定义清楚每个计算阶段的数据应该放在哪一层存储,什么时候搬运、什么时候计算、什么时候写回,整个执行序列像排公交车时刻表一样。丧失了版本管理,随时回退。
以后有条件我再单独展开几篇:第四篇想写AI芯片编译器后端的指令调度优化,第五篇想写多芯片互联的软硬件协同,第六篇则打算回到垂直行业,聊聊具体场景里软硬件设计的取舍案例。你有兴趣的话,建议先找几块真实设备,把你手上的模型用profiler跑一遍,看看瓶颈到底在哪一步——这个习惯比读十篇架构分析都管用。