开头
过去这一年,和做 AI 部署的朋友聊天,十有八九绕不开同一句话:模型越做越大,卡越买越贵,但真正让人失眠的已经不完全是“算力不够”,而是“内存不够了”。你囤了一堆算力卡,结果显存插槽被模型权重塞得满满当当,连腔调浮点格式的余地都没有;刚想拉长上下文,KV Cache 又直接把显存余额清零。这种情况见多了,我越来越确信,AI 产业已经实打实地进入了一个“内存焦虑”时代。
这里说的“内存焦虑”,不是你电脑开太多网页那种小打小闹,而是整个行业在模型规模呈指数级膨胀之后,撞上了一面叫“内存墙”的硬墙。算力需求还有并行计算可以分摊,内存需求和显存带宽却卡在物理规律和造价成本上。大量工程师每天的主要工作,从调参调模型,变成了抠显存、算内存占用、量化权重、换并行策略,甚至为了省几个 GB 重新设计服务架构。这篇文章,我就以这几年踩坑和调优的实际经验,把内存焦虑这件事拆开揉碎讲清楚:它到底卡在哪些环节、为什么 CPU 和 GPU 都到了极限、我们在部署实践中可以用哪些办法把内存消耗压下来,以及真实环境里你会撞上哪些经典问题。
不管你是做模型部署的大模型工程师,还是刚接触推理优化的技术负责人,又或者只是被“显存爆炸”折磨过的算法同学,这篇应该能给你一张比较完整的路线图。
1. 从“算力焦虑”到“内存焦虑”:最大瓶颈变了
1.1 为什么算力问题还没解决,内存先告急
先说一个最基础的常识:深度学习模型跑起来,尤其是大模型推理,内存要装的东西不只是“模型本身”。过去做 CV 小模型,一个几百 MB 的模型丢进显存,剩下的空间随便造。但到了大模型时代,情况完全变了。
我经常用一个很土的比喻解释这件事:算力是工厂里的机器速度,内存就是工厂的仓库。以前仓库小但订单也少,勉强够用;现在订单量暴涨,仓库却没按同样的速度扩容。大模型推理时,权重参数要驻留在显存里,每一层计算都要反复读取。如果没有足够大的仓库,哪怕机器速度再快,也得停下来等数据搬运。
举个例子你就明白数字有多夸张:一个 70B 参数级别的模型,用 FP16 精度存储,光权重就占 140GB 左右。这是什么概念?很多单张旗舰级加速卡的显存是几十 GB 级别,单卡根本放不下,必须用多卡切分才能加载。这还只是权重,还没算上计算过程中的中间激活值、优化器状态、服务端缓存等。所以很多人以为“有算力就能跑”,实际上第一关就是显存容量够不够装。
算力焦虑还有办法缓解:多买卡、多并行,总能堆上去。内存焦虑却更别扭——单张卡的显存容量技术演进没那么快,容量不够时,工程师只能围着权重格式和缓存策略反复折腾,把本来能跑通的架构硬生生改成“省内存特供版”。
1.2 内存焦虑到底发生在哪几层
为了不让自己一锅粥,我把大模型训练和推理过程中的内存占用拆成四层,逐层看才好在工程上对症下药。
第一层是权重存储。模型参数量乘以字节数,就是最底层的硬开销。FP16 下每个参数占 2 字节,70B 模型就是 140GB;如果用 INT8 量化,能压缩到 70GB;再激进一点用 INT4,大约 35GB。这一层的优化思路主要围绕精度换空间,后续我会详细算账。
第二层是 KV Cache。这是解码阶段最容易被忽略、却最致命的开销。Transformer 模型生成每个 token 时,都要把历史上所有 token 的 Key 和 Value 缓存下来,用它算当前 token 的注意力。序列越长,KV Cache 膨胀得越厉害,而且它跟模型层数、注意力头数、批次大小都线性相关。很多场景下,模型权重没爆炸,KV Cache 先把显存塞爆了。
第三层是激活值。训练时,前向计算产生的中间激活值必须存下来做反向传播;推理时虽然不需要反向,但某些算子中间结果也会占额外临时空间。像超长序列做注意力计算时,注意力矩阵的峰值内存可能高得吓人,这层优化往往靠算子融合和重计算来压。
第四层是系统级内存。在 CPU 和 GPU 混用架构中,GPU 显存不够了,就会把部分数据和临时结果换到 CPU 内存,这个交换过程靠 PCIe 或 CXL 总线。看似扩充了可用容量,可是总线带宽比显存带宽低几个数量级,一旦频繁换入换出,性能直接下落,这也会制造“内存焦虑”。
把这四层摆在台面上,你就会发现,模型部署优化本质上就是一门把“内存账本”算清楚的手艺。你别只盯着模型精度提升,内存账本算不明白,再好的模型也部署不出去。
2. 内存墙:带宽、容量和成本的三角博弈
2.1 内存墙是怎么出现的
“内存墙”这个词不是新概念,早在 CPU 性能时代就有,但 AI 时代把它重新推到了聚光灯下。
简单说,过去二十年,芯片算力增长遵循摩尔定律,每年都稳定提升;内存带宽和容量的增长却远远跟不上。算力相当于高速公路上的车速,内存带宽相当于高速宽窄。车速越来越快,可路面宽度没有等比增加,结果就是大量时间浪费在“等数据”。这就是著名的“内存墙”问题。
放到大模型场景里,墙的高度更夸张。Transformer 模型的计算密集度特别高,注意力矩阵和全连接层的算力需求增长快,但模型参数和 KV Cache 都要高带宽随机访问内存。业内常用“算力/内存比”(Arithmetic Intensity,算术密度)来衡量:如果你的模型运算强度高于硬件平衡点,那计算单元在空转,等内存送数据。
用生活例子类比:内存带宽就是传送带,从内存仓库把数据搬到计算工厂。仓库里东西再多,传送带不够宽,所有算力都得排着队等。这也是为什么 GPU 厂商在拼命堆内存带宽,但内存墙只是从“矮墙”变成了“高墙”,没有消失。
2.2 高带宽内存的代价:性能很美,价格很痛
既然内存带宽不够,行业给出的直接方案是“高带宽内存”,就是通过堆叠 DRAM 颗粒并用硅通孔技术互联,把带宽做到远高于普通内存的水平。比如某个体系下的高带宽内存带宽可以达到数 TB/s 级别,相比传统内存提升好几倍。
可问题在于,高带宽内存实在太贵了,而且产能紧张。堆叠层数越多,良率越难做,成本指数级上升。一块 AI 加速卡里,高带宽内存的成本占比相当可观,这也直接推高了整卡价格。你去看云上算力租赁价格,带大容量高带宽内存的实例,费用明显高出普通实例一大截。
从产业博弈看,这里有一个死结:内存焦虑本质上是成本焦虑。容量要够,带宽要快,价格还得压下来,这三者很难同时满足。某存储大厂的供应策略也主要向利润更高的产品倾斜,导致下游芯片厂商能拿到多少货、什么价格拿货,直接决定他们的产品定位。
我在实际项目里体会最深的是,选卡的时候不能只看算力 TFLOPS,还要把显存带宽、显存容量和单位显存成本一起算。有一些算力卡纸面算力很高,但显存带宽不足,跑大模型长序列时性能反而打折扣。这就是为什么业内开始流行一个观点:大模型选型,先看内存,再看算力。
2.3 为什么参数规模继续膨胀会碰壁
模型参数规模的扩张,不是简单的“仓库多放几层货”就行。推理时的内存需求有两个维度:参数带来的静态内存呈线性增长,但上下文处理带来的动态内存开销增长更猛。特别是在注意力机制下,输入序列长度增加时,计算复杂度和 KV Cache 都是不可忽视的关键变量。
说白了,模型越大,需要的权重空间越大;输入序列越长,需要的缓存空间越大。这两个因素叠加,让 GPU 显存像雨季的蓄水池,明明看起来很大,水位涨得比谁都快。
更麻烦的是,单纯堆参数已经在走向收益递减。很多团队发现,模型参数涨了 50%,推理时的显存需求也涨了 50%,可实际生成质量并没有等比提升。于是行业开始转向“稀疏化”“MoE 化”等设计,比如混合专家模型,每次只激活一部分专家参数。这样一来,虽然总参数量很大,但实际参与计算的参数少,权重装载压力得以缓解。不过 MoE 也有自己的内存难题:所有专家权重都要放进显存,否则来回换专家也会拖垮性能。
内存焦虑并不是“忍一忍就能过去”的状态,它正在反向塑造模型架构的走向。未来谁能把内存账本做平,谁就能在成本和性能的夹缝中活得舒服。
3. 实操:让现有显存“多撑一点”的四条路径
3.1 先把格式打下来:量化和混合精度的账本
说到降低内存,第一个能立刻做的事就是量化。量化本质上是“降低数字精度,用更少字节表态一个权重”。FP32 一个参数占 4 字节,FP16/BF16 占 2 字节,INT8 占 1 字节,INT4 只占 0.5 字节。
动手算一下最直观。还是拿 70B 模型举例:
- FP16:70 × 10^9 × 2 字节 = 140GB
- INT8:70 × 10^9 × 1 字节 = 70GB
- INT4:70 × 10^9 × 0.5 字节 = 35GB
也就是说,同样一个模型,从 FP16 压到 INT8,显存刚好砍半;再压到 INT4,又砍半。很多消费级显卡原本跑不动的模型,量化成 INT4 后就能硬塞进去,本地跑的很多 7B 模型也是靠这个思路省出来的。
量化不是无损的,尤其对超大规模模型。实际操作里,我最常用的做法是“混合量化”:哪些层对精度敏感就保留高精度,哪些层冗余度高就量化得狠一点。比如注意力层通常比全连接层对精度更敏感,量化时就要谨慎。前些年业界常用“训练后量化”,模型权重直接转格式,不需要重新训练,速度快;但如果你追求更高质量,就需要做“量化感知训练”,让模型在训练阶段就适应低精度带来的扰动。
这里必须提醒一句:量化能省容量,但不一定省带宽。虽然 INT8 权重在总线上传输的字节量更少,但如果你的量化算法让某些算子变得低效,实际推理速度未必提升。所以每次量化后都要跑一遍基准测试,看吞吐量、看首 token 时延,别只盯着参数体积。
3.2 控制 KV Cache 这个“长序列杀手”
我在生产环境里遇到的绝大多数显存溢出,罪魁祸首不是权重,而是 KV Cache。这个问题在长对话和长文档场景里格外严重。
给你一个估算公式,我每次做容量规划都会用它:
KV Cache 字节数 = Batch Size × 序列长度 × 层数 × 每头维度 × 注意力头数 × 2(Key 和 Value 各一份)× 精度字节数
举个例子,假设某模型有 32 层,32 个注意力头,每头维度 128,按 FP16 计算,批大小为 1,生成长度为 4096。那么公式是:
1 × 4096 × 32 × 128 × 32 × 2 × 2 字节 = 约 8.6GB
这只是“一个请求”的 KV Cache。如果并发增大到 16,单是 KV Cache 就逼近 137GB,这个数字比绝大多数卡本身的显存还大。所以你就会明白,为什么“支持长上下文”在工程上这么烧内存。
在我自己压显存的时候,KV Cache 有很多常规优化手段:
- KV Cache 量化:把缓存中的 Key 和 Value 压成 INT8,甚至 FP8,精度损失往往很小。由于 KV Cache 占用动态增长,量化它能带来比权重量化更直观的收益。
- 共享前缀和 Prompt Cache:微调或少样本场景里,很多请求共用相同前缀,缓存这些前缀的 KV 结果,就不需要重复计算。
- 分页管理:把 KV Cache 按页划分,动态分配。这类似操作系统的“虚拟内存”思想——避免固定预留一大块空间,有效提升显存利用率和并发吞吐。
- 长度走廊限制:在业务层直接限制模型最大输出长度和上下文上限。在能接受的情况下,限制到 4K 往往比 32K 的体验差不了太多,显存却省出好几倍。
讲到这里顺便提一句,我试用过几个推理加速方案,都提供了类似的“KV 复用”功能。如果你的业务有大量重复系统提示词,这个功能几乎白赚性能,一定要打开。
3.3 并行与调度:把多卡的内存拼在一起
单卡显存放不下所有模型权重怎么办?最直接思路就是多卡并行。大模型部署最常见的两种并行方式:
- 张量并行:把一组矩阵运算切到多张卡上,每张卡持有权重的一部分,最终结果合并。比如 2 卡并行时,一张 70B 模型的 FP16 权重从 140GB 降到每卡 70GB 左右。
- 流水线并行:把模型一层层拆开,不同层放在不同卡上。第 1 层在第 1 张卡,第 2 层在第 2 张卡,数据按顺序流过。它能大幅减少单卡的权重内存压力,但由于流水线存在天然“气泡”效应(前后级卡忙闲不均),吞吐可能打折。
张量并行的问题是引入了大量通信,卡之间要频繁同步数据,对总线带宽和互连拓扑要求很高。卡数越多,通信开销越摊薄不了。流水线并行的问题是内存均衡性难控制,容易出现某张卡是瓶颈。
实际工程中,两者经常搭配使用。我们的经验法则是:先按每张卡的显存容量决定要拆几份;如果并行度太高,就引入流水线并行削峰。部署之前先用公式估算权重内存加 KV Cache 加激活值的总量,再除以目标单卡可用显存的 70% 左右,多留一点余量给临时算子,不然上线第一分钟就炸。
最近还有一类工程方案越来越流行,叫“连续批处理”。传统批处理把所有请求固定到一批,等最长的那条生成完才算完,其他请求只能干等,显存也被长序列请求占满。连续批处理则是指当一个请求序列生成结束,立刻让新请求插进空闲显存槽位,整个利用率能提升好几成。很多开源推理引擎都在内部做了优化,你只需要在部署时打开对应开关,效果立竿见影。我做过一次对比,同配置下打开连续批处理后,单卡吞吐提升超过一倍。
3.4 业务侧取舍:上下文和批次其实是经营决策
每次做性能调优,技术人员总想把所有参数拉满,但真实业务里,上下文长度和并发数都不是技术指标,而是经营决策。
我遇到过一个很典型的长上下文案例:业务方要求模型必须支持 32K 上下文,结果显存不管怎么优化都不够。后来我们复盘用户真实数据,发现 95% 的实际请求不到 4K 长度。于是我们拆成两个模型:小上下文模型负责大多数请求,长上下文模型只给真正需要的低频查询用。整块成本直接下降了一半。
这背后的逻辑很简单:内存是有限资源,如果你对所有请求都按最坏情况分配显存,那大部分显存可能都在空转浪费。最好的优化不是卡里塞最满,而是用容量规划把资源精准匹配到真实需求上。
另一个决策点是 Batch Size。很多人以为 Batch 越大越好,但实际上每个子请求都会单独占据一部分 KV Cache,大 Batch 意味着更大的内存峰值。GPU 算力高自然希望 Batch 大一点,这能提升吞吐;但内存墙却要求 Batch 收敛。最好的做法是根据每张卡的空闲显存动态调整 Batch,不要用固定值。推理框架的调度器本质上就是在为你做这种动态博弈。
4. 常见问题与排查实录
4.1 经典现场:为什么一张大卡还是跑不动
有一天同事跑过来跟我说,新买的大显存卡,跑一个 13B 模型,居然直接 OOM。我上去看了下负载,第一反应不是模型太大,而是它把 KV Cache 预留得太夸张了。
排查过程大概是这样的:先看单卡显存总量,再看加载权重后剩余显存,最后看推理请求的并发数和序列长度。我用一条简单命令观察显存占用变化,发现请求数量一上来,显存使用曲线直线飙升,这说明很大一块空间都被缓存动态吃掉了,而不是模型权重。
解决方案也是典型三板斧:先把 KV Cache 的量化打开;再限制单请求的最大长度;最后把并发度调低一点。一轮操作下来,同样的卡跑同样的模型,稳定服务几十个并发请求,内存占用时刻健康。
这个案例我印象很深,因为“显存大”和“能跑大模型”是两回事。有时候你以为瓶颈是容量,其实是管理和分配策略的问题。用工具看清内存去向,永远比盲目换卡更有用。
4.2 常见问题速查表
我把平时在生产环境里碰到的高频问题汇总成一张表,你可以直接贴到自己的排障手册里:
| 问题场景 | 典型原因 | 解决方向 |
|---|---|---|
| 加载模型时直接 OOM | 模型权重体积超过单卡显存 | 做 INT8/INT4 量化,或改用张量并行切分权重 |
| 对话变长后显存暴涨 | KV Cache 随长度线性膨胀 | KV Cache 量化、启用分页调度、限制最大长度 |
| 并发一高就显存溢出 | 每个请求独立占缓存,Batch 过大 | 改用连续批处理,动态调整并发上限 |
| 单卡显存仍有空余,但推理很慢 | 数据搬运受限,内存带宽成为瓶颈 | 检查模型是否频繁在 CPU/GPU 间换入换出,减少跨卡通信 |
| 量化后体积变小,但速度没提升 | 算子兼容性或精度损失导致重计算 | 跑基准测试找热点算力瓶颈,开混合精度 |
| 请求有大量相同系统提示词 | 每个请求重复计算相同前缀 | 开启提示词缓存,复用 KV |
| MoE 模型总显存监控很高 | 所有专家权重必须常驻显存 | 规划专家放置策略,避免频繁交换专家缓存 |
这张表的经验是:大部分“内存焦虑”问题都可以通过“先量化、再改缓存策略、最后限并发”的思路逐步收敛。如果这三招都用了还不行,再考虑加卡或者换卡。
4.3 面向未来的缓解方向:新存储和存算一体
聊完眼前的落地经验,也想聊聊长期趋势。行业里已经在探索几个可能根本改变内存焦虑的方向。
一是内存池化。把多个节点的空闲内存通过网络互联,形成一个大型内存池,计算节点可以通过低延迟协议访问。当前不少新的互连和内存协议都在朝这个方向走。这样做的价值在于,某张卡显存不够时,不是只能干瞪眼,而是可以“借”别的节点的内存。不过带宽仍有限,实际性能还需要看应用场景。
二是存算一体。把存储单元和计算单元做在同一个物理区域,让数据不用搬远路,直接就地计算,能大幅缓解内存带宽焦虑。当前还有很多工程挑战,比如精度控制和制造成本,但这是从根上对抗内存墙的思路之一。
三是模型结构本身的改革。比如更激进的稀疏化、更多条件计算、混合专家模型的工程化成熟。从根源减少“必须常驻内存的权重量”,而不是一味增加内存容量。我个人觉得,模型结构和内存硬件的协同创新才是最终答案。单一的“堆容量”或者“堆带宽”只能缓解一时。
结尾
做 AI 部署这些年,我最深的体会是:真正难的不是把模型训练出来,而是把它稳定、便宜、不崩溃地放到线上。内存焦虑这个东西,短期看是显存容量告急的报错,长期看是硬件供给和算法需求之间的深层错配。跟它对抗,光靠买更多的卡不现实,更靠谱的办法是把每一块显存都当作稀缺资源去精细管理。
从个人实际项目经验出发,我给你的建议很简单:先动手把显存账本算清楚,量化权重、压缩 KV Cache、动态调整并发,这三板斧能解决八成焦虑;再往后,就要关注模型结构和硬件发展的趋势,提前做技术储备。内存墙不会自己在某一天消失,但你的应对体系可以先它一步成熟起来。最后再分享个小技巧——优化完一定记得测端到端延迟和吞吐,别只盯着显存数字看,省了内存却慢了响应,才是真正得不偿失。