简介:一份聚焦大模型硬件算力瓶颈的技术论文PDF,面向AI芯片设计、体系架构研究人员及对大模型推理优化感兴趣的技术开发者。论文围绕ChatGPT等比年指数级增长带来的超大算力需求与数据中心数据传输压力展开,提出基于存算一体集成芯片的专用硬件架构;核心内容包括存算一体的电路与架构协同思路、集成芯粒(Chiplet)扩展方案,以及轻量化-存内压缩协同设计如何将稀疏网络稠密映射到存算一体硬件,以提升存储密度与能效比。压缩包内为单一PDF文档,大小3.49MB,共1个文件,内容来自《中兴通讯技术》2024年4月期刊,含中文摘要、英文Abstract、引用格式、图表数据与关键技术论述,适合用于了解前沿AI加速硬件设计趋势、撰写论文或做技术预研时快速查阅。已有137人学习,可作为参考。
1. 存算一体为什么成了大模型专用硬件架构里绕不开的一环
模型越训越大,算法越改越巧,可真正跑大模型推理时最磨人的却不是算子太少,而是数据搬运太慢。一颗GPU的算力再高,把权重从HBM搬到计算单元这件事本身就要吃掉大量时间和功耗,这就是业内常说的“内存墙”。而基于存算一体集成芯片的大模型专用硬件架构,核心思路就是不再把存储和计算分成两块,而是让存储阵列本身去完成乘加运算,把“搬数据”变成“在数据里算”。这篇笔记会沿着这条路线,讲清楚存算一体是什么、为什么它对大模型是“专用”而非“通用”,以及一套可以照着做的落地路径。适合正在做AI芯片产品规划、加速卡选型评估,或者负责大模型推理部署平台的工程师看,也适合想摸清这块硬件底细的算法同学。
2. 把大模型的瓶颈拆开看:为什么偏偏是内存墙先卡住
2.1 Transformer的访存特征:权重搬运量比计算量更致命
大模型推理和传统卷积神经网络有个很不一样的特征:单次前向计算里,权重会被反复读取。以7B参数为例,INT8量化后权重就有7GB上下,即使一张H100也只有80GB显存。推理时每生成一个token,都要把所有层的权重完整过一遍。如果批量大小是1(很多交互式场景就是这样),那算力其实只利用了很小一部分,绝大部分时间都耗在读权重上,这一现象通常被称为“访存密集”。
反过来看传统GPU的架构,计算单元和存储层次之间隔着多层缓存和总线,一次矩阵乘法的数据要从片外HBM一级一级搬进来。存算一体集成芯片的做法,是直接把权重“烧制”在存储阵列里,激活值流过阵列时,位线和字线之间的物理效应就完成了乘加,输出的依然是电信号。这样省掉的不是计算本身,而是把权重从片外搬到寄存器的这一整套流程。
这个差别对大模型是致命的。因为Transformer里最重的两部分,注意力机制的QKV投影和MLP的上百亿参数矩阵乘,其本质都是大矩阵乘,而且权重静态不变。静态权重天然适合驻留在存算阵列中。这也解释了为什么过去几年行业里讨论大模型硬件加速,凡是提到存算一体,几乎都默认是奔着大模型的权重矩阵乘场景去的,而不是为了跑卷积。
2.2 存算一体的三条路线:模拟、数字、混合,各自适合什么
目前做存算一体集成芯片,主流路线可以分成三类。第一类是模拟存算,主要用RRAM、MRAM、FeFET这类新型存储器件组成交叉阵列,激活值按模拟电压送入,列电流的累加结果就是矩阵乘法的结果。它的密度高、理论能效好,但精度受器件一致性和温度漂移影响,需要ADC/DAC在边界做数模转换,开销不小。
第二类是数字存算,典型做法是用SRAM单元组成计算阵列,把乘法在数字域完成。它的精度可控,工艺成熟,但单个存储单元面积大,容量做不高。第三类则是混合方案,把模拟阵列和数字SRAM、甚至传统逻辑核放在同一颗芯片上,权重按精度要求分门别类。
对大模型来说,我的经验是:如果目标是跑7B以上模型且希望整个模型尽量驻留在片上,模拟路线在密度上更占优势;如果目标是做小参数模型或主打精度稳定,数字方案更稳。至于混合方案,是大模型专用硬件里最容易出东西的形态,因为它可以把不同精度的算子和不同层分开处理,比如把易受干扰的归一化层留在数字侧,把权重占比最大的线性层放到模拟阵列。
选型时还有个容易被忽视的参数:写入功耗和写入次数。RRAM器件写一次功耗高、寿命有限,而大模型推理场景权重更新频率远低于训练场景,正好合适。反过来,如果拿存算一体去跑在线训练,那写入次数和写后精度一致性就会变成主要矛盾,这一点需要在架构设计之初就明确。
2.3 为什么“大模型专用”而不是“通用AI加速器”
通用加速器追求什么算子都能跑,这就意味着它的存储层次、调度器、数据通路上必须保留很多“万一用得上”的机制,成本都摊在每一笔访存里。而大模型专用硬件不问别的,只看一件事:这个Transformer最重的算子是什么、数据流长什么样。权重共享、KV Cache渐进增长、生成阶段批量小,这些特征一旦被固化成硬件参数,芯片面积和功耗都可以大幅削减。
举个例子,很多存算一体芯片的片上存储设计成“激活跨层直通”的结构,跳过传统L2缓存。这对CNN是灾难,但对Transformer完全没问题,因为激活值只在相邻层之间流动,没有跨层复用。这就是“专用”的真正含义——为了任务特征做减法。
表:主流存算一体技术路线对比
| 路线 | 存储介质 | 精度表现 | 容量密度 | 写入代价 | 适合大模型的场景 |
|---|---|---|---|---|---|
| 数字CIM | SRAM | 高,可支持INT8 | 低 | 低 | 小模型、精度敏感、KV Cache近存 |
| 模拟CIM | RRAM | 中,需校准 | 高 | 高 | 权重驻留、大模型线性层 |
| 模拟CIM | MRAM | 中高 | 高 | 中 | 需要一定写次数的权重更新 |
| 模拟CIM | FeFET | 中,多级态 | 高 | 中高 | 多比特权重、存内逻辑 |
| 混合 | SRAM+RRAM | 按分区 | 按分区 | 按介质 | 完整的LLM推理SoC |
3. 把大模型映射到存算阵列:Attention、MLP和KV Cache的具体切分法
3.1 先定任务映射,再谈芯片参数
拿到存算一体集成芯片,第一件事不是看算力峰值,而是先把目标大模型的网络结构展开,统计每一类的算子和数据量。以大模型常见的两层结构为例,自注意力层的QKV投影是权重静态的矩阵乘,注意力分数计算和Softmax是非线性的,MLP的前两层同样是大矩阵乘,而最后的归一化、残差连接几乎全是按元素操作。一个合理的映射策略是把所有矩阵乘塞进存算阵列,把元素级操作留在附近的数字逻辑里,把KV Cache放到离计算阵列足够近的存储块中。
这里有个比例关系很关键:大模型推理中,线性层参数量占比通常超过95%,而按token数摊下来,KV Cache的访存量又会随序列长度线性增长。所以硬件上不能只把权重放进去就完事,KV Cache的容量和带宽需要单独预算,否则序列一长就掉速。
3.2 一个最小可用的任务切分伪代码
下面这段伪代码示意的是一个简化版注意力层在存算阵列上的切分步骤。它不做完整模型前向,只用来表达权重复用、激活缓存和批处理边界:
# 假设目标为 7B 模型中的单层注意力,head_dim=128,seq_len=2048,batch=1 tile_m = 128 # 沿序列维度的分块大小 tile_k = 128 # 沿权重维度的分块大小 kvcache_policy = "layer_local" # "layer_local" 表示KV驻留在本层存储 def attention_forward(q, k_cache, v_cache, w_qkv, w_o): # w_qkv: [hidden, 3*hidden] 的静态权重,已预置在CIM阵列 x = cim_gemm(q, w_qkv) # 一次CIM矩阵乘,q流入位线,权重驻留 q, k, v = split_last_dim(x, 3) for seq_blk in range(0, seq_len, tile_m): # 分块读取缓存中的K,避免一次把整序列放进阵列 scores_blk = cim_gemm(q, k[:, seq_blk:seq_blk + tile_m].T) scores = softmax(scores_blk, dim=-1) ctx_blk = cim_gemm(scores, v[:, seq_blk:seq_blk + tile_m]) out = combine_blocks(ctx_blk) return cim_gemm(out, w_o)这段代码的逻辑是:QKV投影和输出投影走存算阵列,Softmax留在数字侧处理,KV按序列分块循环读取。好处是避免了把整条序列的KV一次性搬入计算单元,降低了对片上缓存容量和CIM阵列规模的压力。实际芯片上,Softmax虽然只占整体计算量的很小比例,但它需要的动态范围比较大,用模拟阵列做指数运算容易飘,数字侧做更稳。这个伪代码里的三个参数值得细调:tile_m直接决定KV分块粒度,tile_k决定权重阵列利用率,kvcache_policy则影响上下文窗口。模型越大,序列越长,tile_m就应该调大以减少重复读取K的次数,但代价是需要更大的片上临时缓存,两者的平衡要靠实测数据定。
3.3 KV Cache在存算硬件里的布局优先级
KV Cache在推理中增长速度极快,以7B模型、序列长度4096为例,单条请求的KV占用约几十MB量级。如果沿用传统架构把KV放在片外,那么每个生成步骤都要把这几十MB读出来,访存压力非常大。存算一体芯片的一个优势是:可以把KV Cache做成“近存计算”形态,也就是让缓存块具备轻量计算能力,只做查找和掩码这类简单操作,同时保证带宽足够高。比把它塞进模拟存算阵列更合理,模拟器件做KV这类频繁读写场景寿命和精度都不友好,更适合放到SRAM还是数字侧。
另一个容易被忽略的问题是跨层KV是否需要共享。多数大模型每层都有独立KV,因此层次之间要预留足够的总线带宽。如果带宽不够,那么即便算力再高,生成阶段依然会被KV读取拖慢。实际解决时常用做法是给每层KV分配独立的SRAM分区,调度器只在层间切换时切换指针,不在物理上搬数据。
3.4 生成阶段的“小批量困境”
批量大小对存算一体芯片的影响被很多人低估。预填充阶段批量可以很大,适合做高吞吐的GEMM;但生成阶段一次只算一个token,批量几乎等于1,这时候矩阵乘变成了GEMV——矩阵乘法的行向量乘列向量。存算阵列对GEMV的支持度取决于能不能把同一个权重阵列同时喂给多个序列,也就是多请求并行。如果芯片只按GEMM优化,生成阶段会发现峰值算力远用不上,实际掉速明显。
应对做法是在架构设计就把“批处理调度器”做成动态的:预填充来的大任务切成整列处理,生成阶段的小任务合并成虚拟批次,让同一个权重阵列同时服务多个请求。这个技巧在最火的部署框架里很常见,但在存算芯片里实现要更小心,因为它要求阵列的输出端能做多目标累加而不互相干扰。
4. 大模型部署侧的参数窗口:位宽、阵列尺寸、片上存储比与可行性估算
4.1 四个必调参数:权重位宽、激活位宽、阵列尺寸、片上存储比
存算一体芯片被做成产品后,模型部署者能调的参数其实不多,但每一个都影响吞吐和模型质量。第一是权重位宽,常用INT8或INT4。大模型的权重分布相对集中,INT4在很多层上都能用,但个别层(尤其是开头几层embedding)对量化敏感,容易造成整条链路质量下滑。因此实际工程里往往做混合位宽:大部分线性层走INT4,敏感层走INT8。
第二是激活位宽,激活的量化比权重难点多。模拟存算里激活是以电压送入阵列的,ADC位数决定了读出精度,位宽不够时矩阵输出会带截断噪声,累积到后面的归一化层可能引发“全链路输出偏移”。经验值上,权重INT4时激活至少要保持INT8以上,ADC建议不低于6位。
第三是阵列尺寸,也就是单个CIM宏的矩阵规模。常见的是128×128或256×64。尺寸越大,单次能算的矩阵块越大,但它的功耗和良率压力也越高。部署时如果模型的hidden_dim不是阵列尺寸的整数倍,就会产生填充浪费。第四是片上存储比,指SRAM和CIM阵列总容量占模型权重的比例。7B模型INT4量化后大约3.5GB,而一颗存算芯片片上容量通常在几十MB到几百MB,放不下时就要“分层驻留”:常驻层放高频权重,冷层放低频权重,层切换靠片外加载。这个比值直接决定推理延迟曲线的形状。
表:7B模型INT4量化后部署参数参考
| 参数项 | 建议值 | 影响 | 失败表现 |
|---|---|---|---|
| 权重位宽 | INT4/INT8混合 | 容量和精度 | 回答质量下降、重复输出 |
| 激活位宽 | INT8 | 精度与ADC成本 | 困惑度飙升、生成停顿 |
| ADC位数 | 6-8bit | 读精度 | 输出抖动、PPL异常 |
| 阵列Tiling | 128/256 | 利用率 | 算力上不去 |
| 片上存储比 | ≥1/8权重总量 | 层切换频率 | 生成极慢、带宽打满 |
4.2 一份可行性估算脚本:真理永远在预算表里
动手买板子或做芯片评估前,先用一个简单的Roofline估算脚本看瓶颈在哪。下面这段Python脚本可以快速判断“在给定芯片参数下,部署7B模型是受算力限制还是受带宽限制”:
import numpy as np def roofline_inference(params_mb, compute_tops, bw_gbps, token_batch=1): # 参数: 7B模型INT4权重约3500MB,INT8约7000MB weight_bytes = params_mb * 1024 * 1024 # 生成阶段: 每个token读取全部权重一次 read_bytes_per_token = weight_bytes * token_batch compute_ops_per_token = 2 * params_mb * 1024 * 1024 / (1024**3) * 1e9 # 约FLOPs估算 # 算力时间 compute_time = compute_ops_per_token / (compute_tops * 1e12) # 秒 # 带宽时间 bandwidth_time = read_bytes_per_token / (bw_gbps * 1e9) # 秒 bottleneck = "compute" if compute_time > bandwidth_time else "bandwidth" return bottleneck, compute_time, bandwidth_time # 模拟一颗存算芯片: 100 TOPS, 500 GB/s 片内聚合带宽 for tops, bw in [(100, 500), (50, 1000), (200, 2000)]: result, t_c, t_b = roofline_inference(3500, tops, bw, token_batch=1) print(f"TOPS={tops}, BW={bw}GB/s -> 瓶颈={result}, 计算={t_c*1e3:.2f}ms, 搬运={t_b*1e3:.2f}ms")这段脚本逻辑很简单:生成阶段每生成一个token,理论上要读一遍全部权重,因此带宽时间就是权重总量除以存储带宽。如果模型好几百GB而芯片带宽只有几百GB每秒,那计算再快也白搭。实际运行7B模型时,脚本输出的“搬运时间”如果超过“计算时间”一个量级,那就别急着调阵列尺寸,先解决片上存储容量或增加HBM带宽预算。真实项目里这个估算常常让人清醒,所谓几百TOPS的芯片,落到单用户交互场景里,可能连每秒十个token都出不来。脚本里有两个隐含假设要留意:一是认为权重必须整读,实际上如果做了算子切分和层驻留优化,搬运量可以降低;二是没有计入KV Cache的读取,序列越长越偏向带宽瓶颈。因此在看任何芯片的白皮书时,先跑一遍这个预算公式,再去看宣传的峰值算力,心理落差会小很多。
4.3 精度验证:用困惑度而不是单个算子误差
大模型对硬件非理想误差的容忍度比其他模型低,但又不是简单线性降低。项目里常遇到的一个情况是:单独测某一个CIM宏的矩阵乘精度,误差在1%以内,看起来很好;但整模型跑起来,生成的文本开始重复、无逻辑甚至全中文。原因是模拟器件的误差不是白噪声,它带有记忆性和漂移,与模型权重的分布相乘后会在某些层被放大。所以在评估存算一体芯片时不要只跑算子的信噪比,要跑端到端的困惑度,用一套固定测试集对比量化前后PPL变化。PPL值上升超过5%就要警惕。这条经验放在任何宣称“高精度”的存算芯片上都适用,也是避免被测试报告误导的底线。
5. 大模型硬件化的避坑清单:量化崩溃、模拟漂移和查不到的诡异卡顿
5.1 模拟阵列精度“看起来”很好,端到端生成却翻车
- 现象:单算子误差在可接受范围,但用存算一体芯片跑大模型时,生成文本质量异常,出现乱码、重复循环或中英文混杂。
- 原因:模拟阵列误差并非均匀分布,某些权重值对应的电导状态漂移大,且随着温度和使用时间变化。Transformer的残差结构会把这种误差层层累积,到深层变成不可忽略的偏移。
- 解决:不要只信单算子测试,必须做端到端PPL验证。同时为阵列设计周期校准流程,在部署前加入“冷启动自检”,校准电导矩阵。另一个补救是识别敏感层,把PPL贡献大的层保留在数字SRAM上跑,不放进模拟阵列。
5.2 KV Cache容量被乐观估计,长序列直接掉到个位数token每秒
- 现象:短序列时速度正常,序列一长,生成时延指数级上升,甚至报“out of memory”。
- 原因:KV Cache大小随序列长度线性增长,但片上存储往往优先留给了权重,KV分区预算明显不足。生成阶段每个token都要读写整段KV,容量不够时只能换出到片外,访存翻几倍。
- 解决:部署前先按“目标最大序列长度”计算KV Cache需求,做分层缓存策略。老token压缩放入片外存储,最近窗口保留在片上。如果KV Cache需求的10%以上要放片外,建议干脆降低最大上下文长度,否则生成延迟没法看。
5.3 生成阶段的瓶颈不是TOPS,而是激活读出
- 现象:芯片标称100 TOPS,但单用户生成速度不到预期的一半,看起来像是软件没调好。
- 原因:生成阶段batch=1导致以GEMV为主,矩阵乘计算量很小,但激活和中间结果的读出次数不变,存储带宽反而成为瓶颈。
- 解决:在硬件架构里增加多条请求并发,采用虚拟批处理,把多个GEMV合并成一个轻量GEMM。软件侧要确认调度器是否开启了这种合并,不能把预填充和生成混在一个静态配置里。
5.4 激活位宽降低后PPL只涨了一点,但输出开始“结巴”
- 现象:把激活从INT8降成INT6,PPL指标勉强还在接受范围,但实际工程中生成速度不稳定,偶发长时间停顿。
- 原因:激活位宽降低后,非线性层如GELU、Softmax的输入噪声增加,模型输出的token概率分布变得扁平,采样时需要更多候选,造成“卡顿感”。PPL是平均指标,无法反映分布尾部的恶化。
- 解决:只看PPL不够,要加“最长相同前缀连续生成一致性”指标。如果是模拟阵列,优先保持激活位宽不变,只调整权重位宽。
5.5 层切换卡顿:权重“没搬完”成了隐藏瓶颈
- 现象:模型精度正常,吞吐也算得过去,但每一次层与层切换都有一个可以感知的停顿,尤其在长序列时明显。
- 原因:大模型层数多,如果权重不是全部驻留片上,每层计算前需要从片外加载权重。为了省带宽,很多实现是“用多少搬多少”,而层间依赖关系又导致无法预取,最终表现为“算5毫秒,等10毫秒”。
- 解决:在软件调度上做“下一层权重预取”,把权重加载和当前层计算重叠。这个操作不算难,但需要硬件提供双缓冲通道。如果芯片不支持,就要靠调整层驻留顺序,把高频层固定放片上,低频层放片外,减少切换频率。
6. 验证存算一体方案到底行不行:一套可复用的微基准与端到端对照法
大模型硬件项目最怕的就是“纸面参数很漂亮,落地数据打对折”。我的习惯是设计一套三层验证流程,从微基准到端到端逐级收紧。第一层是单算子效率测试,瞄准的是GEMM/GEMV在存算阵列上的实测Tops以及能耗,但只看趋势,不看绝对峰值。第二层是单层Transformer前向测试,把注意力、MLP、归一化都串起来,重点观察层间数据流是否被带宽卡死。第三层是整模型端到端生成,记录prefill延迟、decode延迟、以及PPL和生成文本质量三个指标。三层各自独立,但只有第三层能拍板“行不行”。
这里分享一个具体技巧:在端到端测试时,要做“量化回退对照”实验。也就是把同一套模型分别跑在“全INT4模拟存算”和“权重全INT8、激活INT8、Softmax保留FP16”的配置上,生成相同输入的多条回答,逐条对比重点语句的语义一致性。这个对照能准确找出精度损失的真正来源,是来自量化位宽还是来自模拟阵列漂移。另一个实用技巧是在部署脚本里加“每层误差快照”,把每层输出与参考输出(FP32)的余弦相似度存成日志。一旦生成质量变差,直接定位到哪一层开始恶化,极大减少排查时间。
做了几个代际的存算一体芯片评估之后,我的体会是这个领域没有银弹:存算一体适合大模型是因为它重新平衡了访存和计算,但它也会把器件的物理非理想性变成软件系统里最磨人的隐患。所以真正可靠的做法不是指望某一家芯片完美,而是把验证流程做深,宁可多花一周测PPL,也不要被峰值TOPS蒙蔽。希望这一套方法和避坑经验能帮到你。
本文还有配套的精品资源,点击获取