1. 为什么说推理芯片的瓶颈往往不在算力本身
很多人第一次接触推理芯片选型时,习惯性地先看算力指标——TOPS 数字越大越好,主频越高越强。这个思路在训练场景下或许还站得住脚,但放到推理场景里,十有八九会踩坑。我见过不止一个团队,选了一款标称算力很漂亮的芯片,结果实际部署后发现吞吐量连理论值的三成都不到,延迟波动还特别大。排查到最后,问题几乎都指向同一个地方:存储子系统的设计没有跟上计算单元的需求。
推理和训练对硬件的诉求有本质区别。训练阶段追求的是极致算力和大规模并行,数据可以提前准备好、批量灌进去,对延迟不敏感;而推理阶段面对的是真实业务流量,请求随机到达、批量大小动态变化、对首包延迟和长尾延迟都有硬性要求。这时候,计算单元再强,如果数据喂不进去或者喂进去的节奏不对,算力就是空转。用个生活化的类比:推理芯片的计算单元像是一个手艺极快的厨师,存储子系统就是传菜和备菜的通道。厨师刀工再快,如果配菜员每次只递一根葱、或者递菜的顺序乱七八糟,出菜速度照样上不去。
存储子系统在推理芯片里承担的角色远比很多人想象的复杂。它不只是“把数据存下来、需要的时候取出来”这么简单,而是要同时解决好几个层面的问题:权重参数的存放和读取、激活值的暂存与复用、输入输出数据的搬运、多核之间的数据同步、以及和外部主存之间的带宽分配。每一个环节的设计取舍,都会直接反映到最终的推理性能上。这也是为什么同样一颗计算核心,搭配不同的存储架构,实测性能可以差出好几倍。
我在实际项目中总结出一条经验:评估一颗推理芯片时,先别急着看算力峰值,而是去翻它的存储子系统规格——片上缓存多大、带宽多少、访存粒度多细、有没有针对常见算子做数据复用优化。这些信息往往比 TOPS 数字更能说明它在真实场景下的表现。接下来的内容,我会从存储子系统的几个关键维度展开,把“为什么存储设计决定推理性能”这件事拆开讲透,同时给出选型和优化时可以直接参考的判断依据。
2. 推理场景下存储子系统的四层结构拆解
2.1 寄存器文件与片上缓存:计算单元的“手边抽屉”
最靠近计算单元的是寄存器文件,它直接参与乘加运算,速度最快但容量极小,通常只有几十到几百字节。再往外一层是片上缓存,一般分为权重缓存和激活缓存两块。权重缓存存放的是模型参数,推理过程中这些数据是只读的,所以设计上可以做成高带宽、低功耗的专用 SRAM;激活缓存则要支持读写,存放的是每一层的输入输出特征图,容量需求随批量大小和特征图尺寸动态变化。
这两块缓存的容量配比非常讲究。权重缓存太小,大模型就得频繁从外部存储搬参数,带宽压力陡增;激活缓存太小,批量稍微大一点就放不下,只能拆成多次计算,核间同步开销立刻上升。我见过一款芯片,权重缓存给得很足,但激活缓存只有权重缓存的三分之一,结果在批量大于 4 的时候性能断崖式下跌。后来查手册才发现,它的设计初衷是面向单样本低延迟场景,压根没考虑大吞吐的批量推理。
注意:看芯片规格时,不要只看“总缓存大小”这一个数字,一定要区分权重缓存和激活缓存各自多大,以及它们是否支持动态分配。有些芯片标称 8MB 缓存,实际权重和激活是固定切分的,灵活性很差。
2.2 多级缓存与数据复用:减少“跑腿”次数的关键
片上缓存之外,很多推理芯片还会设计一级或两级中间缓存,用来做数据复用。推理计算有一个特点:同一块输入数据往往会被多个卷积核或注意力头反复使用。如果每次使用都从外部存储重新读取,带宽浪费会非常严重。中间缓存的作用就是把这部分复用数据留在离计算单元更近的地方,减少对外部带宽的占用。
数据复用的粒度设计直接影响缓存命中率。粒度太粗,缓存里塞了很多用不上的数据,有效容量下降;粒度太细,访存次数增加,控制逻辑变复杂。比较成熟的做法是按算子类型做差异化设计:卷积层按行或按块复用,注意力机制按头或按序列片段复用。我在调优一个视觉推理模型时发现,把特征图按 8x8 块做缓存对齐后,外部带宽占用下降了将近四成,端到端延迟也跟着降了 15% 左右。这个收益不是来自算力提升,纯粹是存储访问模式优化带来的。
2.3 外部主存接口:带宽与延迟的平衡术
片上存储再大也放不下整个模型,最终还是要和外部主存打交道。推理芯片的外部存储接口设计,核心是在带宽和延迟之间找平衡。带宽决定了单位时间能搬多少数据,延迟决定了单次访问要等多久。对于大模型推理,带宽往往是第一瓶颈;对于小模型低延迟场景,延迟更关键。
这里有一个容易被忽略的点:外部存储的访问效率不仅取决于接口理论带宽,还取决于实际访问模式。随机访问和顺序访问的有效带宽可以差出好几倍。推理过程中,权重读取通常是顺序的,效率较高;但激活值的读写模式取决于算子实现,如果实现得不好,大量随机访问会把有效带宽拉低到理论值的一半以下。所以评估芯片时,除了看接口标称带宽,还要关注它有没有针对推理常见访问模式做优化,比如是否支持大块连续传输、是否有预取机制、是否对稀疏访问有特殊处理。
2.4 片间与片内互联:多核协作的“交通规则”
当单颗芯片的算力不够时,多核甚至多芯片协作就成了必然选择。这时候,片内互联和片间互联的存储一致性设计就变得至关重要。多核同时访问共享数据时,如果没有合理的同步和一致性机制,要么出现数据冲突导致结果错误,要么过度同步导致性能崩塌。
我参与过一个多核推理系统的调试,最初版本里每个核独立从外部存储读权重,结果外部带宽被抢得一塌糊涂,多核性能反而比单核还差。后来改成权重只读一份、通过片内高速互联广播给各核,带宽压力立刻缓解,多核加速比才回到正常水平。这个案例说明,存储子系统的设计不只是单核内部的事,多核之间的数据共享和同步策略同样属于存储架构的核心范畴。
3. 从实测数据看存储设计如何左右推理吞吐
3.1 一个视觉推理模型的带宽瓶颈复现过程
为了把这个问题讲清楚,我拿一个中等规模的视觉推理模型做过一组对比测试。模型本身计算量不算大,单核算力完全够用,但实测吞吐始终上不去。用性能分析工具抓取访存数据后发现,计算单元有超过 60% 的时间在等数据,真正在计算的时间不到 40%。进一步拆解,等待主要发生在两个地方:一是权重读取,二是特征图写回。
权重读取的问题出在缓存容量上。这个模型的权重总量超过了片上权重缓存的容量,导致每一轮推理都要从外部存储重新加载部分权重。外部存储的带宽虽然不低,但延迟远高于片上缓存,计算单元频繁陷入等待。后来通过权重量化把模型压缩到缓存容量以内,等待时间立刻大幅下降,吞吐提升了将近一倍。这个测试很直观地说明了一个道理:存储容量不够,算力再强也发挥不出来。
特征图写回的问题则和访问模式有关。原始实现里,特征图写回是按像素逐个写的,外部存储的有效带宽只有理论值的 30% 左右。改成按行块批量写回后,有效带宽提升到 70% 以上,写回耗时缩短了一半多。这两个优化都没有改动计算逻辑,纯粹是存储访问层面的调整,但带来的性能收益比换一颗算力更高的芯片还明显。
3.2 批量大小变化时存储子系统的表现差异
推理场景的批量大小往往是动态的,有时候是单样本低延迟请求,有时候是大批量吞吐请求。存储子系统在不同批量下的表现差异非常大。我记录过一组数据:同一颗芯片,批量大小为 1 时,延迟主要花在权重加载上,因为计算量太小,权重加载时间占比超过一半;批量增大到 16 时,权重加载被摊薄,激活值读写成为主要瓶颈;批量继续增大到 64 时,片上激活缓存不够用,开始频繁换入换出,延迟反而上升。
| 批量大小 | 主要瓶颈 | 延迟变化趋势 | 优化方向 |
|---|---|---|---|
| 1 | 权重加载延迟 | 基准 | 权重缓存常驻、预取 |
| 4 | 权重加载摊薄 | 下降 | 保持权重缓存命中 |
| 16 | 激活值读写带宽 | 继续下降 | 激活缓存复用优化 |
| 64 | 激活缓存容量不足 | 反弹上升 | 分块计算、减少换入换出 |
这张表说明,存储子系统的设计必须考虑目标场景的批量分布。如果业务以低延迟单样本为主,权重缓存和预取机制是重点;如果以高吞吐批量为主,激活缓存容量和带宽才是关键。一颗芯片不可能在所有批量下都表现最优,选型时要先明确自己的业务画像。
3.3 多核扩展时存储带宽的分配策略
多核推理芯片的存储带宽分配是一个容易被低估的难题。单核时带宽够用,不代表多核时也够用。多核同时跑推理,每个核都在抢外部存储带宽,如果没有合理的分配和调度机制,就会出现“核越多、单核性能越差”的尴尬局面。
我见过两种典型的带宽分配策略。一种是静态划分,每个核固定分到一部分带宽,互不干扰,但灵活性差,某个核带宽不够时其他核的闲置带宽用不上。另一种是动态共享,所有核共用一个带宽池,按需分配,灵活性好,但需要硬件支持优先级和公平调度,否则容易出现某个核饿死的情况。实际产品里,做得好的芯片通常采用混合策略:权重读取走静态划分保证确定性,激活值读写走动态共享提高利用率。
从优化角度,如果芯片支持带宽分配配置,建议根据模型结构做调整。权重占比大的模型,给权重读取多分一些带宽;激活值占比大的模型,给激活值读写多分一些。这个配置往往能在不换硬件的情况下带来 10% 到 20% 的性能提升。
4. 选型与优化中那些容易踩的存储相关坑
4.1 只看容量不看带宽和延迟的选型误区
选推理芯片时,存储规格表上通常列了容量、带宽、延迟三个指标,但很多人只盯着容量看。容量当然重要,它决定了模型能不能完整放进片上存储,但带宽和延迟同样关键。我遇到过一款芯片,片上缓存容量很大,但带宽只有同类产品的一半,结果大模型推理时计算单元经常处于“有数据但搬不快”的状态,实际性能反而不如缓存小但带宽高的竞品。
更隐蔽的是延迟指标。有些芯片标称带宽很高,但访问延迟也很大,适合大批量顺序访问,不适合小批量随机访问。如果你的业务是低延迟单样本推理,高带宽高延迟的存储设计反而会成为拖累。所以选型时一定要结合自己的业务场景,把容量、带宽、延迟三个维度放在一起权衡,而不是单独看某一个。
4.2 忽视访存对齐导致的有效带宽损失
访存对齐是一个看起来很小、实际影响很大的问题。很多芯片的存储接口要求访问地址按特定边界对齐,比如 32 字节或 128 字节。如果数据存放地址没有对齐,一次访问可能被拆成两次,有效带宽直接减半。这个问题在手工优化算子时特别容易出现,因为开发者往往只关注计算逻辑,忽略了数据布局的对齐要求。
我的经验是,在模型部署前先检查所有张量的内存布局,确保关键数据按芯片要求的边界对齐。对于不满足对齐要求的数据,要么在加载时做一次重排,要么在模型转换阶段就调整好布局。这个工作看起来繁琐,但带来的带宽收益非常可观。我曾经通过调整特征图的内存对齐方式,把某个算子的执行时间缩短了 30% 以上,改动量却只有几行代码。
4.3 多核共享缓存的一致性开销被低估
多核推理芯片通常有共享缓存,用来存放多个核都需要访问的数据,比如公共权重或全局激活值。共享缓存的好处是节省容量、方便同步,但代价是一致性维护开销。当多个核同时读写共享缓存时,硬件需要保证数据一致性,这个过程的开销在核数增多时会显著上升。
我调试过一个四核推理系统,最初把所有权重都放在共享缓存里,结果四核同时推理时性能只有单核的 2.2 倍,远低于预期的 3.5 倍以上。分析后发现,一致性维护占用了大量互联带宽。后来把权重拆成四份,每个核独享一份,只在必要时同步少量全局数据,加速比才提升到 3.2 倍。这个案例说明,共享缓存不是越多越好,要根据数据访问模式决定哪些数据共享、哪些数据独享。
4.4 存储子系统与算子实现的匹配问题
最后一个坑是存储子系统和算子实现的匹配。同一个算子,用不同的数据布局和访问顺序实现,在同一个存储子系统上的性能可以差很多。比如矩阵乘法,按行优先还是按列优先访问,缓存命中率完全不同。如果算子实现没有针对芯片的存储特性做优化,即使芯片存储设计很好,也发挥不出来。
比较务实的做法是,在选定芯片后,优先使用芯片厂商提供的优化算子库,这些库通常已经针对存储特性做了调优。如果需要自己实现算子,一定要先了解芯片的缓存行大小、访存粒度、预取策略这些细节,然后据此设计数据布局和循环顺序。我见过太多团队在算子实现上照搬通用写法,结果性能只有优化版本的几分之一,还以为是芯片不行。
5. 面向存储优化的推理部署实操建议
5.1 模型转换阶段的存储布局预处理
模型转换是存储优化的第一个窗口期。在这个阶段,可以根据目标芯片的存储特性,对模型做布局重排、权重量化、算子融合等预处理。布局重排的目的是让数据在内存中的排列方式更符合芯片的访存偏好,比如把卷积权重从 NCHW 转成 NHWC,或者按缓存行大小做填充对齐。权重量化则是把浮点权重转成定点或低比特格式,直接减少存储占用和带宽需求。
我在实际项目里通常会做三件事:第一,统计各层权重和激活值的实际大小,确认哪些层会超出片上缓存;第二,对超出缓存的层做权重量化或分块处理;第三,检查所有张量的对齐情况,对不满足要求的做重排。这三步做完,往往能在不改变模型精度的前提下,把推理性能提升 20% 到 50%。关键是这些工作都在模型转换阶段完成,不影响运行时逻辑,部署起来很干净。
5.2 运行时权重常驻与预取策略配置
运行时阶段,存储优化的重点是权重常驻和预取。权重常驻是指把频繁使用的权重固定在片上缓存里,避免反复从外部存储加载。预取则是在计算单元还需要某块数据之前,提前把它从外部存储搬到片上缓存,用搬运时间掩盖计算时间。这两个策略配合使用,可以显著降低计算单元的等待时间。
配置权重常驻时,要优先选择那些访问频率高、复用次数多的层,比如 Transformer 里的注意力权重、卷积网络里的前几层权重。预取策略则要根据数据依赖关系来设计,预取太早会占用缓存,预取太晚来不及。我的经验是,预取距离设置为当前计算块之后的一到两个块比较合适,具体数值需要根据芯片的存储延迟和计算速度做微调。很多芯片厂商的 SDK 里已经提供了预取配置接口,直接调用比自己实现更稳妥。
5.3 批量调度与存储压力的动态平衡
批量调度是运行时优化的另一个抓手。批量越大,计算单元利用率越高,但存储压力也越大。当片上激活缓存不够时,大批量会导致频繁换入换出,反而降低性能。所以批量大小不是越大越好,而是要找到一个和存储容量匹配的平衡点。
实际操作中,我会先测出芯片在不同批量下的性能曲线,找到性能拐点。然后在部署时,根据请求到达速率动态调整批量:请求密集时用较大批量提高吞吐,请求稀疏时用较小批量降低延迟。有些推理框架支持动态批处理,可以自动做这个调整,但默认配置往往不是最优的,需要根据存储规格手动调参。我调过一个动态批处理配置,把最大批量从 32 降到 16,虽然单次吞吐略降,但因为避免了缓存溢出,整体吞吐反而提升了 18%。
5.4 性能分析工具在存储瓶颈定位中的用法
定位存储瓶颈离不开性能分析工具。好的分析工具能告诉你计算单元等了多久、等的是哪块数据、缓存命中率是多少、外部带宽利用率是多少。这些信息是优化存储子系统的依据。我常用的分析维度包括:计算单元活跃周期占比、各级缓存命中率、外部存储有效带宽、访存延迟分布。
分析时有一个技巧:先看计算单元活跃周期占比,如果低于 70%,说明存储瓶颈明显;然后看各级缓存命中率,定位是哪一级缓存出了问题;最后看外部存储有效带宽,判断是带宽不够还是访问模式不好。按照这个顺序排查,基本能覆盖大部分存储相关问题。需要注意的是,不同芯片的分析工具接口不一样,有些需要厂商提供的专用工具,有些可以通过通用性能计数器读取。部署前最好先确认分析工具是否可用,否则优化会变成盲人摸象。
6. 存储子系统设计趋势对推理部署的长期影响
6.1 近存计算与存内计算对推理架构的改变
近存计算和存内计算是这两年存储子系统设计的热门方向。近存计算是把一部分计算逻辑搬到存储旁边,减少数据搬运;存内计算则是直接在存储单元里做计算,彻底消除搬运开销。这两个方向对推理部署的影响是深远的:如果计算和存储的边界模糊了,传统意义上“计算单元等数据”的瓶颈就会消失,推理性能的天花板会被重新定义。
从部署角度看,这类新架构对模型布局和算子实现提出了新要求。数据不再需要严格按照计算单元的偏好来排列,而是要考虑存储单元的计算能力。比如存内计算适合做矩阵向量乘这类规则运算,部署时就要把模型里对应的算子映射过去。目前这类架构还在演进中,生态工具链不如传统架构成熟,但长期来看,它可能会改变推理芯片的选型逻辑——从“选算力”变成“选存储计算匹配度”。
6.2 高带宽存储堆叠在推理芯片中的角色
高带宽存储堆叠技术把多层存储芯片垂直堆叠,通过硅通孔连接,能在很小的面积里提供极高的带宽。这项技术已经在一些高端推理芯片上落地,效果非常明显:外部存储带宽不再是瓶颈,计算单元可以更充分地发挥算力。对于大模型推理来说,高带宽存储堆叠几乎是刚需,因为大模型的权重和激活值总量太大,传统存储接口的带宽根本喂不饱计算单元。
不过高带宽存储堆叠也有代价:成本高、散热难、容量扩展受限。所以它更适合对性能要求极高、对成本相对不敏感的场景。部署时要注意,高带宽存储堆叠的访问延迟虽然比传统外部存储低,但仍然高于片上缓存,所以权重常驻和预取策略依然重要。不能因为带宽高了就忽视访问模式优化,否则有效带宽还是上不去。
6.3 存储子系统可编程性对部署灵活性的提升
越来越多的推理芯片开始提供可编程的存储子系统,允许开发者根据模型特点自定义缓存分配、预取策略、数据布局。这个趋势对部署灵活性是很大的提升。以前芯片的存储架构是固定的,开发者只能去适应它;现在可以反过来,让存储架构适应模型。
可编程存储子系统的用法通常是提供一套配置接口或微码,开发者通过配置来描述数据流和缓存策略。这要求开发者对模型结构和芯片存储架构都有深入理解,门槛不低,但收益也大。我试过用可编程缓存配置把一个混合模型的推理性能提升了 40% 以上,关键就是把不同算子的缓存需求分别配置,而不是用一套通用策略。这个方向值得投入时间学习,尤其是当你的模型结构比较特殊、通用优化手段效果有限时。
6.4 从存储视角重新理解推理芯片选型逻辑
把上面这些内容串起来,推理芯片的选型逻辑其实可以重新梳理一遍。算力仍然是基础,但存储子系统的权重应该被提到同等甚至更高的位置。具体来说,我会按这个顺序评估:先看片上缓存容量和带宽能否覆盖目标模型的主要数据需求;再看外部存储接口的带宽和延迟是否匹配业务场景;然后看多核互联和一致性机制是否支持预期的扩展规模;最后看存储子系统是否可编程、是否有配套的优化工具和算子库。
这个顺序和很多人习惯的“先看算力再看存储”正好相反,但更符合推理场景的实际瓶颈分布。算力决定上限,存储决定能不能达到上限。一颗算力中等但存储设计优秀的芯片,在真实推理场景里往往比算力高但存储拉胯的芯片表现更好。这个判断我在多个项目里验证过,几乎没有例外。所以下次选型时,不妨先把存储规格翻出来仔细看看,再回头看算力数字,你可能会做出不一样的决定。