实战对比:Lmcache+vllm将KVcache卸载到CPU和SSD的性能差异(附完整配置)
最近在做大模型推理服务优化的时候,发现一个非常现实的问题:GPU显存根本不够用。尤其是跑长上下文、高并发的场景,显存被KV cache占掉一大半,并发稍微调高一点就直接OOM。后来试了LMCache把KV cache从GPU显存卸载到CPU内存和SSD上,效果确实立竿见影,但CPU和SSD这两条路线的性能差异比我预想中大得多。
这篇文章就把我亲手跑出来的对比数据、完整配置、以及踩过的坑一次说清楚。无论你是做推理服务部署的工程师,还是在折腾本地大模型、被显存逼疯的研究员,这篇都能给你一个明确的选型参考。
先说结论:如果你有富余的内存条,优先把KV cache卸载到CPU;只有内存也吃紧的时候才考虑SSD,且一定要用NVMe接口的盘。SSD方案能救急,但扛不住高并发和长序列,延迟波动也明显。具体数据往下看。
1. 为什么需要KV cache卸载:显存瓶颈的本质
在聊LMCache之前,先把KV cache这个东西掰扯清楚。大模型推理时,每次生成一个token都需要重新计算之前所有token的Key和Value向量。为了不重复计算,推理框架会把历史token的K、V值缓存下来,这就是KV cache。它有多吃显存?以7B模型为例,序列长度2048、并发8个请求时,KV cache轻松占掉十几GB显存,比模型权重本身还多。
显存里的空间是固定的,模型权重、激活值、KV cache三者互相抢地盘。权重不能动,激活值不敢省,最容易被牺牲的就是KV cache。所以传统做法是限制并发数,或者把最大序列长度调低。但这直接牺牲了服务的吞吐能力和用户体验——并发高一点就爆显存,上下文长一点就报错。
LMCache的思路很直白:既然显存不够,就把KV cache挪到更便宜、更大的存储介质上,也就是CPU内存和磁盘。它和vLLM的集成是原生级别的,通过在KV cache传输路径上做hook,把最不活跃的KV block换出到CPU内存或SSD,要用的时候再换回来。听起来简单,但实现上有几个关键设计直接影响性能:缓存换入换出的调度策略、分块粒度、以及后端存储的读写效率。
vLLM 0.5.0之后把LMCache作为官方推荐的KV cache卸载方案,原理是给每个KV block标记一个访问热度,LRU策略把冷块优先卸到CPU或磁盘。这样GPU显存只保留热块,相当于给显存加了个“外置缓存”,容量天花板一下子被打碎了。
不过,放到CPU和放到SSD完全是两个量级的事情。CPU走的是内存总线,带宽可以达到几十GB/s,延迟是纳秒级;SSD哪怕是最好的NVMe,顺序读也就3-7GB/s,延迟是微秒级。所以LMCache在CPU和SSD上的表现差异,本质上就是这两条硬件路径的代差。
在动手之前,我给自己的优化目标定了个基准:在显存不变的前提下,把并发数从原来的4路提到16路以上,同时保证首token延迟在可接受范围内。如果你也有类似目标,下面的对比过程可以直接抄作业。
2. 测试环境与工具链选型解析
没有靠谱的环境,性能对比就是耍流氓。我这次用的是单机双卡A100 80G的服务器,但其实80G显存还是不够跑长文本大并发,恰恰是这种“显存看起来不小、实际还是不够”的场景,最能暴露KV cache卸载的价值。
2.1 软硬件配置一览
硬件部分:
- GPU:NVIDIA A100 80G x 2,驱动535.104.05
- CPU:AMD EPYC 7543 32核
- 内存:512GB DDR4 3200
- SSD:三星PM9A3 1.92T NVMe(系统盘和数据盘分离)
- 网卡:Mellanox ConnectX-6 100GbE
软件部分:
- Ubuntu 22.04 LTS,内核5.15
- CUDA 12.1,PyTorch 2.1.2
- vLLM 0.5.4(LMCache集成版)
- LMCache 0.1.4
- Python 3.10
老实说,vLLM和LMCache的版本匹配是最大的坑点。LMCache 0.1.x对应vLLM 0.5.x,如果你用的是vLLM 0.6.x,就要升级到LMCache 0.2.x。版本对不上,vLLM启动的时候直接报AttributeError,根本起不来。这个后面章节详细说。
2.2 LMCache的核心配置项解读
LMCache装好之后,通过vLLM启动命令里的环境变量控制,最关键的有三项:
| 环境变量 | 作用 | 可选值 | 我的推荐 |
|---|---|---|---|
LMCACHE_ENABLED | 总开关 | 0/1 | 1 |
LMCACHE_CPU_OFFLOAD_BUFFER_SIZE | CPU卸载缓冲池大小 | 0-显存上限(GB) | 显存1/4 |
LMCACHE_DISK_OFFLOAD_ENABLED | 磁盘卸载开关 | 0/1 | 视测试项而定 |
LMCACHE_DISK_OFFLOAD_DIR | 磁盘缓存目录 | 任意路径 | 建议单独挂载NVMe分区 |
缓冲池大小是需要解释的概念。LMCache不是把所有KV cache都搬到CPU,而是在GPU显存里开一块固定大小的“中转站”,临时放下被换出的KV block。这个值设太小,SSD写入会频繁触发,性能反而下降;设太大,留给模型权重的显存不够,影响batch size。我测试下来,设成显存容量的1/4效果最好。比如80G显存,LMCACHE_CPU_OFFLOAD_BUFFER_SIZE=20。
还有一个隐藏参数容易被忽略:LMCACHE_CHUNK_SIZE,KV cache分块大小,默认是16个token一块。这个值直接影响换入换出的粒度,块越大传输效率越高,但热度判断越粗糙;块越小调度越灵活,但会引入更多元数据开销。实测下来,长文本场景用32更好,短文本16就够了。我这个测试模型用的是32。
2.3 测试方法与压测脚本设计
我用的是vLLM自带的Benchmark脚本做压测,这套脚本的好处是可以同时拿到TTFT(首token延迟)、TPOT(每个输出token延迟)和吞吐量,指标维度比较完整。
压测模型选的是Meta-Llama-3-8B-Instruct,8B规模适中,KV cache的显存占用在长文本场景下很典型。输入长度固定在2048,输出长度定为128,模拟的是RAG场景下的问答请求,每轮请求都要带一段很长的上下文。
压测请求数设成200条,并发分别测了1、4、8、16、32五个档位。因为LMCache对高并发更友好,所以低并发时性能退化不明显,一旦并发上去,CPU和SSD的差异就会快速拉开。
3. 完整配置方案与启动命令拆解
这一节直接上干货。我演示三套配置:基线方案(纯GPU)、CPU卸载方案、SSD卸载方案。每套都是可以直接复制运行的完整命令,并解释每个参数为什么这么设。
3.1 基线方案:纯GPU运行
先看不启用LMCache的基线,这是所有对比的地基,也是后面判断性能损失的参照物。
python -m vllm.entrypoints.openai.api_server \ --model /data/models/Meta-Llama-3-8B-Instruct \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.90 \ --max-model-len 32768 \ --max-num-seqs 16 \ --enable-prefix-caching三个参数需要说明:
--tensor-parallel-size 2:双卡张量并行,这个不用多说--gpu-memory-utilization 0.90:vLLM允许使用90%的显存,留10%给CUDA context和其他开销。如果这个值太高,LMCache做CPU卸载的时候没有足够空间做中转,所以后面配置里我调成0.75--enable-prefix-caching:这里先开启,因为RAG场景会有大量重复的前缀,这个参数和LMCache叠加效果更好
启动后看到init engine (finished with 0.00 second)就算成功了,接着可以并发压测。
3.2 CPU卸载方案:内存当显存用
这是我最推荐的生产环境方案。把KV cache卸载到CPU内存的前提是内存足够大,实测下来8B模型配256G内存,16并发下完全无压力。
LMCACHE_ENABLED=1 \ LMCACHE_CPU_OFFLOAD_BUFFER_SIZE=20 \ LMCACHE_CHUNK_SIZE=32 \ python -m vllm.entrypoints.openai.api_server \ --model /data/models/Meta-Llama-3-8B-Instruct \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.75 \ --max-model-len 32768 \ --max-num-seqs 32 \ --enable-prefix-caching注意这里三个关键变化:
第一,LMCACHE_CPU_OFFLOAD_BUFFER_SIZE=20,也就是20GB的GPU显存作为中转池。为什么不是更大?因为--gpu-memory-utilization 0.75意味着vLLM能支配60GB,模型权重和激活值大概吃掉30GB,剩下的30GB里有20GB做中转池,再留10GB作为调度余量。
第二,--max-num-seqs 32,并发上限直接翻倍。这就是卸载KV cache的核心收益——显存被释放出来,可以容纳更多并发请求。8B模型在32并发下每请求分配到的显存依然够用。
第三,--gpu-memory-utilization从0.90降到0.75,这是故意为之。LMCache在需要换出KV block时,要先在GPU里拷贝block,然后再传到CPU。这个拷贝动作需要临时显存,留太少会直接报CUDA OOM。我试过0.85配合LMCache,跑20轮就崩了,降到0.75才稳。
CPU卸载方案生效后,逻辑上KV cache分成两层:热块留在GPU,冷块放CPU。因为内存读写带宽极高,换入换出的调度开销远小于GPU显存空间的收益,所以16并发以内几乎感知不到性能退化。
3.3 SSD卸载方案:把显存让给权重
内存也不够用了怎么办?上SSD。SSD方案适合显存和内存同时紧张的极端场景,比如跑几十B的大模型,或者需要超长上下文的场景。
LMCACHE_ENABLED=1 \ LMCACHE_CPU_OFFLOAD_BUFFER_SIZE=5 \ LMCACHE_DISK_OFFLOAD_ENABLED=1 \ LMCACHE_DISK_OFFLOAD_DIR=/ssd_cache/lmcache \ LMCACHE_CHUNK_SIZE=32 \ python -m vllm.entrypoints.openai.api_server \ --model /data/models/Meta-Llama-3-8B-Instruct \ --tensor-parallel-size 2 \ --gpu-memory-utilization 0.75 \ --max-model-len 32768 \ --max-num-seqs 32 \ --enable-prefix-cachingSSD方案的缓冲池只留了5GB。原因是LMCache有层级策略,KV block先尝试驻留GPU,放不下就落到CPU内存,CPU内存再满才写SSD。所以缓冲池不需要太大,5GB只是个临时接力区,SSD才是真正的容量兜底。
还有一个设置:/ssd_cache/lmcache这个目录,我特意用mkfs.ext4格式化了一块独立的NVMe分区,而不是用根目录。原因是LMCache写KV cache是持续高IO,如果和系统盘抢IO带宽,SSD性能会被严重拖累,而且KV cache文件量很大,塞系统盘会影响其他服务。
SSD方案还有个隐藏风险:磁盘碎片。LMCache的KV block是固定大小的chunk文件,频繁写入删除会产生大量碎片。我用了fstrim定时回收,并且LMCache也提供了LMCACHE_MAX_DISK_SIZE限制,默认是200GB,建议根据你的磁盘容量和KV cache大小单独设置。
选型参考:如果你只是偶尔跑一次超长任务,SSD方案够用;如果是长期高并发的生产服务,老老实实加内存。
4. 性能数据对比:实测结果与关键指标拆解
终于到重点了。我跑了200条请求,涵盖了纯GPU、CPU卸载、SSD卸载三种方案,并发从1到32。下面是整理后的关键数据。
4.1 首Token延迟对比
TTFT是最能直观反映“用户等待感”的指标,直接决定对话式AI的响应体验。
| 并发数 | 纯GPU(ms) | CPU卸载(ms) | SSD卸载(ms) |
|---|---|---|---|
| 1 | 285 | 312 | 358 |
| 4 | 310 | 356 | 489 |
| 8 | 342 | 411 | 687 |
| 16 | 398 | 537 | 1142 |
| 32 | 512 | 748 | 2196 |
从数据看,CPU卸载在低并发时几乎等价,SSD在低并发时有轻微劣化,但还能接受。但等到16并发以上,SSD的TTFT直接飙到1秒以上,32并发到了2.2秒,这已经明显影响用户体验了。
为什么差距这么大?TTFT延迟由两部分组成:模型权重加载和前向计算时间,加上之前所有token的KV cache的加载传输时间。纯GPU方案KV cache全在显存里,读取是亚微秒级;CPU方案走PCIe和内存总线,多几十微秒到几百微秒;SSD方案要直接从NVMe盘读KV block,单次读取延迟就在几百微秒量级。而且并发一高,SSD读写排队,延迟非线性恶化。
这里有个关键细节:8个并发时SSD的TTFT只有687ms,好像还将就,但到16并发直接跳到1142ms。因为NVMe盘的并发读写能力有个临界点,一旦超出,SSD内部调度器和FTL的写放大导致性能悬崖式下跌。我用的PM9A3已经是企业级盘,换成消费级盘,8并发就可能跌破体验线。
4.2 吞吐量与每秒请求数对比
吞吐量是另一个关键指标,它决定了同样一台机器能服务多少用户。
| 并发数 | 纯GPU(req/s) | CPU卸载(req/s) | SSD卸载(req/s) |
|---|---|---|---|
| 1 | 1.68 | 1.66 | 1.35 |
| 4 | 5.89 | 5.72 | 4.02 |
| 8 | 10.37 | 10.18 | 6.41 |
| 16 | 16.22 | 19.67 | 9.24 |
| 32 | 18.04 | 28.45 | 8.63 |
这组数据最能说明问题。CPU卸载在16并发时吞吐量反超纯GPU,32并发时达到28.45 req/s,比纯GPU的18.04提升了57.7%。SSD卸载则相当尴尬,16并发时只有9.24,比纯GPU还低,32并发甚至降到8.63。
CPU卸载反超的原因很好理解:纯GPU方案到16并发时,显存里的KV cache已经塞不下了,vLLM只能阻塞等待,吞吐量遇到天花板。CPU卸载方案把冷KV block挪到内存,GPU的compute和KV cache访问任务保持饱和,所以能继续增长。
SSD方案吞吐量上不去,瓶颈在SSD带宽。我们算一笔账:8B模型,2048个输入token生成128个token,每个token的KV cache大小大概是204882*2(层数 * 头维度 * K和V * 数据类型),约2.3MB。一个请求要完整读完整个KV cache,200个并发请求就是460MB的读流量。NVMe顺序读能到3GB/s,但LMCache是按block随机读取的,随机读性能大打折扣,实测只有600-900MB/s,自然扛不住。
4.3 显存占用对比:卸载到底省了多少
性能数据背后还得看到底省了多少显存,这决定你能跑多大的模型和多少并发。
纯GPU方案下,8B模型、32并发、2048长度上下文,KV cache显存占用约38GB,加上权重和激活值,总共约62GB,到了80G卡的边缘。CPU卸载方案,同样条件下KV cache显存占用只有14GB(中转池的active部分),其他全部换到CPU内存。SSD方案更夸张,KV cache落到CPU内存和SSD各一部分,GPU里只有5GB活跃块。
实际观察到的显存占用比:
| 方案 | KV cache显存占用 | 显存占用率 | 可支撑的最大并发 |
|---|---|---|---|
| 纯GPU | 38GB | 90% | 16 |
| CPU卸载 | 14GB | 75% | 64以上 |
| SSD卸载 | 5GB | 60% | 64以上 |
值得注意的是,CPU卸载方案能支撑的并发上限比纯GPU翻了几倍,显存占用率反而更低。因为KV cache像流水一样——GPU里只保留当前活跃请求的block,冷却的立刻换出到内存。后续再加并发,只需要增加CPU内存占用,GPU里的KV cache总大小是恒定的。
这就是LMCache省显存的本质:它不是压缩了KV cache,而是改变了KV cache的驻留位置。GPU只是KV cache的L1缓存,CPU是L2,SSD是L3,容量逐级扩大,速度逐级下降。
4.4 TPOT与长尾延迟:SSD的隐藏代价
TTFT和吞吐量看的是整体表现,TPOT看的是每个token的生成速度,这是流式输出体验的关键。SSD方案在TPOT上的表现更差。
| 并发数 | 纯GPU TPOT(ms/token) | CPU卸载 TPOT(ms/token) | SSD卸载 TPOT(ms/token) |
|---|---|---|---|
| 8 | 18.3 | 19.2 | 28.7 |
| 16 | 21.6 | 22.8 | 41.5 |
| 32 | 25.4 | 27.1 | 58.9 |
SSD的TPOT到了32并发时接近59ms/token,意味着用户看到的文字生成速度只有每秒17个token,大概就是目视打字机的速度。而CPU卸载方案27ms/token,每秒37个token,流畅度还能接受。
另外我测了P99长尾延迟,SSD方案在32并发时高到天际,有请求等了6秒多才出第一个token,而CPU方案最差也就1秒出头。这里的原因是SSD的写放大和GC(垃圾回收)触发了随机长延迟峰值,同一个NVMe盘,平时响应1ms,一旦碰到GC就可能跳到几百ms。性能波动对在线服务是致命的。
所以如果做企业级的RAG服务或者聊天机器人,SSD方案只适合作为冷备策略,不适合作为主力的KV cache卸载路径。
5. 实际操作中遇到的问题与排查实录
性能数据看完了,接下来分享实操中遇到的一堆问题。这些问题文档里不全写,但遇到任何一个都会让你怀疑人生。
5.1 版本不匹配导致报错
我第一次装LMCache 0.2.2配vLLM 0.5.4,启动服务时直接报:
AttributeError: module 'vllm.worker.model_runner' has no attribute 'execute_model'查了半天才发现,LMCache 0.2.x对应vLLM 0.6.x,LMCache 0.1.x对应vLLM 0.5.x,两个版本互相改了很多内部接口,不能乱配。后来把LMCache降到0.1.4,和vLLM 0.5.4配对,问题立刻消失。
这里想提醒一句,LMCache的版本更新很快,每次升级前先去GitHub看Release Notes的兼容性说明。如果你用的是vLLM的源码安装版,还要注意LMCache对vLLM的具体commit有依赖,最好用官方编译好的wheel包。
5.2 显存利用率和缓冲池的平衡难题
我最早配CPU卸载时,把LMCACHE_CPU_OFFLOAD_BUFFER_SIZE设成了显存的一半,也就是40GB,同时--gpu-memory-utilization还保持0.90。启动是正常,跑了50个并发,直接CUDA OOM。
后来意识到,缓冲池占的是GPU显存的实际物理资源。vLLM的gpu-memory-utilization控制总显存使用量,LMCache缓冲池是其中的一部分。如果总使用量是90%,缓冲池又设成40GB,比重太大,模型权重和激活值分配到的显存不够,直接崩。
我的调参经验是:LMCACHE_CPU_OFFLOAD_BUFFER_SIZE不要超过gpu-memory-utilization * 总显存 * 0.33。80G卡用0.75利用率的话,可用显存60G,33%约20G,这和我的实测推荐完全吻合。
如果你要跑更大的模型,比如70B,模型权重本身就占了50-60G,留给KV cache的显存更少。这时候宁可减少缓冲池大小也要保证权重加载,缓冲池可以设为10G甚至5G,让更多KV cache直接落到CPU。
5.3 SSD缓存目录的IO风暴
SSD方案踩过一个大坑:LMCache的默认磁盘路径是~/.lmcache,和系统盘在同一个分区。压测时发现整个系统IO卡死,iostat显示util 100%,CPU的wa指标飙到40%以上,连SSH都开始卡。
解决方法是给LMCache一个独立的NVMe分区。我是这样操作的:
mkfs.ext4 /dev/nvme1n1 mkdir -p /ssd_cache/lmcache mount /dev/nvme1n1 /ssd_cache chmod 777 /ssd_cache/lmcache然后在启动命令里指向这个目录。建议有条件的话,这块盘不要做RAID,直接单盘使用。因为LMCache的KV cache是顺序写随机读的模式,RAID5的写惩罚反而会拖慢速度。
另外SSD方案的磁盘空间管理要提前想好,LMCACHE_MAX_DISK_SIZE默认200GB,如果你用的是1TB的盘,长期跑会写满,LMCache会自动清理最旧的缓存块,但清理GC过程本身也会引发性能波动。我建议根据KV cache的预期大小,把上限设置为磁盘容量的1/3到1/2。
5.4 多卡环境下LMCache的注意事项
双卡跑tensor parallel时,LMCache的KV cache卸载也按卡分别进行。也就是说,每张卡各管各的KV cache卸载任务,卸载到CPU内存时也是按卡分区域存放。配置上不需要额外设置什么,但显存分配上要注意:如果每张卡设LMCACHE_CPU_OFFLOAD_BUFFER_SIZE=20,两张卡一共会占用40GB的CPU内存做中转区,这个内存消耗别忽略。
我实测双卡环境下,CPU方案的内存占用比单卡高了近一倍,512G内存吃掉了80G左右,不过还算充裕。如果你的服务器内存只有128G,建议每张卡的缓冲池设小一点,或者只对主卡启用卸载,另一张卡保持全显存驻留。
6. 热词场景下的LMCache+vLLM实用扩展
顺着热搜词里的一些典型场景,补充几个LMCache+vLLM组合的实战方向,这些场景和我前面压测的数据是对得上的。
6.1 单机多卡部署下的KV Cache卸载策略
之前有一条热搜提到“vllm 单机多卡部署”,就有不少人在多卡环境下配LMCache踩了坑。单机4卡甚至8卡时,LMCache会按GPU实例分别维护KV cache缓冲区,CPU内存的占用是线性增长的。4张80G的卡,每张卡设20G缓冲池,CPU内存就被吃掉80G。如果你的内存没有512G这么大,建议把每卡缓冲池压到10G,并把不常用的卡设成总开关关闭,只保留几张主力卡做卸载。
多卡环境下还有一个容易忽略的点:当tensor parallel size大于2时,模型的KV cache会自动切分到各卡,每个block的数据量变小。LMCache的分块粒度如果不适配这个切分,会降低换入换出的效率。我实测下来,TP=4时LMCACHE_CHUNK_SIZE设为16比32更稳定,因为TP切分后的单卡KV block更小,16个token一块更容易均匀分布。
6.2 长上下文的KV Cache容量规划
很多人用LMCache就是为了跑超长上下文。我试过把max-model-len设到128K,8B模型下,单个请求的KV cache就能到80MB。如果不卸载,显存直接爆;卸载到CPU内存后,128K长度完全放得下。
这时候要算清楚容量:8B模型的KV cache,每1K token约消耗1.15MB(FP16),128K就是147MB。如果目标是100路并发,CPU内存需要准备至少15GB的KV cache容量,加上LMCache缓冲区的元数据开销,建议留20GB余量。SSD方案的话,100路并发128K上下文需要2GB/s级别的读带宽,NVMe勉强能扛,SATA SSD就别想了。
6.3 与SGLang等其他推理框架的定位差异
热搜里出现了“sglang和vllm”对比,这个话题和LMCache也有关联。SGLang有自己的RadixAttention缓存机制,同样支持长上下文复用,但它目前没有像LMCache这样成熟的显存-内存-磁盘三级缓存卸载方案。所以在处理超长上下文+高并发的场景时,vLLM+LMCache的组合更有优势。
但SGLang在调度算法上有自己的独到之处,同样跑长上下文,SGLang的prefill效率可能更高;而vLLM+LMCache的优势是生态成熟、API接口规范,业界部署最广。选型的时候,如果频繁出现“所有请求都带同样的长前缀”这个特征,vLLM+LMCache的prefix caching能给你额外惊喜;如果请求之间前缀复用少,SGLang的前缀树也不一定帮得上忙。
6.4 低显存设备上的降级方案
有些小伙伴在消费级显卡上跑大模型,我也顺手验证了LMCache在小显存环境的表现。用一张RTX 4090 24G跑7B模型,纯GPU方案最多4路并发,启用CPU卸载后可以跑到8路,启用SSD卸载能到12路。但SSD方案的TTFT真的很伤,24G卡配NVMe SSD,8并发时TTFT就已经超过3秒,体验接近不可用。
如果你只有消费级卡,我的建议是:优先加内存条,内存便宜,DDR4 32G不过几百块,比换显卡和SSD划算得多。内存加到64G以上,CPU卸载方案在RTX 4090上跑7B模型8并发是可以接受的。内存确实加不了的话,SSD方案做离线批处理还行,在线服务慎用。
说到最后,我个人的实际体会是:LMCache+vLLM是目前解决大模型推理显存瓶颈最务实的路径。CPU卸载是性能和容量的最佳平衡点,SSD卸载是极端场景下的无奈之举,但至少提供了一条路,让你在显存和内存双重紧张时还能继续跑下去。
如果你准备在生产环境试,先把预算花在内存上,然后再上LMCache,这套组合拳实测下来是最省心且最稳的。到最后再分享一个调试小技巧:启动后用nvidia-smi盯着显存看,如果发现KV cache占用曲线是锯齿形(上去又掉下来),说明缓冲池太小,卸得太频繁;如果是一直满的,说明缓冲池太大,压缩了模型权重空间。调到锯齿逐渐平缓、波动幅度在20%以内,你就算调明白了。