llama-server报错引出的三层缓存理解

在做llama-server压测中遇到高并发与大上下文不能推理直接崩溃问题:

复制代码
 .\llama-server.exe  -m C:\......models\Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf --host 0.0.0.0 --port 8080  -ctk q4_0 -ctv q4_0 -ngl 999   -a Qwen3.6-35B  -np 32 -c 4500000
0.52.356.143 I slot get_availabl: id 31 | task -1 | selected slot by LRU, t_last = -1
0.52.356.191 I slot launch_slot_: id 31 | task 0 | processing task, is_child = 0
0.56.729.005 I slot print_timing: id 31 | task 0 | n_gen =    179, tg =  59.11 t/s, tg_3s =  59.44 t/s
0.56.866.037 I slot print_timing: id 31 | task 0 | prompt eval time =    1361.68 ms /    11 tokens (  123.79 ms per token,     8.08 tokens per second)
0.56.866.051 I slot print_timing: id 31 | task 0 |        eval time =    3148.22 ms /   187 tokens (   16.93 ms per token,    59.08 tokens per second)
0.56.866.052 I slot print_timing: id 31 | task 0 |       total time =    4509.90 ms /   198 tokens
0.56.866.053 I slot print_timing: id 31 | task 0 |    graphs reused =        186
0.56.866.079 I slot      release: id 31 | task 0 | stop processing: n_tokens = 197, truncated = 0
0.57.834.622 I slot get_availabl: id 30 | task -1 | selected slot by LRU, t_last = -1
0.57.834.674 I slot launch_slot_: id 30 | task 189 | processing task, is_child = 0
D:/a/llama.cpp/llama.cpp/ggml/src/ggml-backend.cpp:359: GGML_ASSERT(tensor->data != NULL && "tensor not allocated") failed

 压测指令 evalscope perf   --api openai   --model qwen3.6-35b-q4   --url http://127.0.0.1:8080/v1   --tokenizer-path /....../Qwen/Qwen3.6-35B-A3B   --dataset random   --min-prompt-length 512 --max-prompt-length 1024   --min-tokens 1024 --max-tokens 2048   --prefix-length 0   --parallel 1 2 4 8 16 32   --number 4 8 16 32 64 128   --warmup-num 8   --stream   --duration 300   --visualizer wandb   --name single-short-in-long-out   --wandb-api-key wandb_v1_Qn6P6W29imIJ836NidR

原因:llama-server 自身在多 slot 场景下做 KV-cache / prompt-cache 状态序列化时,读到了一个从未被分配的 tensor,触发 GGML_ASSERT直接 abort 进程。

日志看时序很清晰:

  1. slot 31 正常完成了 task 0 的生成(59 t/s,198 tokens),说明模型加载、warmup、首次推理都没问题;
  2. 随后 LRU 选中 slot 30、launch task 189,还没来得及输出任何 timing 就立刻崩了
  3. 崩溃点 ggml-backend.cpp:359: GGML_ASSERT(tensor->data != NULL && "tensor not allocated"),这行代码位于 ggml_backend_tensor_get/put,是在读写某个 KV-cache tensor 的数据时发现该 tensor 根本没被分配过

关联issue:https://github.com/ggml-org/llama.cpp/issues/26128

llama-server 的 prompt cache 在做 slot 状态保存时走 server_slot::prompt_save()llama_context::state_seq_get_data()ggml_backend_tensor_get()这条序列化路径,而这条路径假设 KV tensor 在主机内存里有直接的 data 指针 。当 KV tensor 落在 RPC 后端(无本地指针)这类"非平凡"后端 buffer 上时,assert 在 buf->iface.get_tensor()(本可以正确处理远端 tensor 的接口)被调用之前就先炸了。

触发条件与你的日志完全对得上 :issue 明确说 -np = 1时潜伏不炸(没有 idle slot 可保存、对话永远命中自己的 slot),-np ≥ 2 时只要有新任务在其它 slot 空闲时进来就立刻 abort。它还列出了两个调用点:LRU 换占 slot 时(get_available_slot())和每个新任务对所有 idle slot 做缓存保存时(process_single_task())。本地日志里 slot 30 被 LRU 选中、launch task 189 即崩,就是这个模式的翻版。

处理方法

  • --cache-ram 0 完全禁用 prompt cache,崩溃消失,作者在并发负载下验证稳定;我验证也是可用的。
  • 另外一种方式是把--np 改16 也是可以正常运行的。
  • 两个重要澄清:--no-cache-prompt 无效 (server prompt cache 是独立机制);--no-cache-idle-slots 只能去掉一个调用点,LRU 路径仍在,不够
  • 关键安慰:slot 内的前缀复用不受 --cache-ram 0影响(实测后续轮次 cache_n=53仍命中),server prompt cache 只在"会话数 > slot 数"时才有收益,对你 32 slot、每 slot 独立请求的压测场景本来就是零收益。

引出 --cache-ram 参数 ,它控制的是 server 的"跨 slot prompt cache"(RAM 侧 KV 状态存档)预算,0 表示完全关闭这套机制;它是独立于 slot 内前缀复用和 --no-cache-prompt的第三个缓存层。

引出了三层缓存机制:

|---------------------------|--------------------------------|------------|-----------|-----------------------------|
| 层级 | 参数 / 开关 | 作用范围 | 存储位置 | 触发场景 |
| Slot 内前缀复用 | 无(自动启用) | 同一 slot 内 | GPU 显存 | 同一对话多轮请求 |
| 跨 Slot RAM 状态存档(cache 主体) | --cache‑ram <MB>(默认 8192MiB) | 所有 slot 之间 | 主机内存(RAM) | 会话数 > slot 数,LRU 换占 slot 时 |
| GPU 侧上下文检查点 | --ctx‑checkpoints <N>(默认 32) | 单个 slot 内 | GPU 显存 | 生成过程中按 token 间隔定期保存 |

--cache-ram 的8192 MiB预算同时服务第二、三层两个机制, 设置 --cache-ram 0同时禁用检查点创建和跨slot缓存。

|--------|---------------------|-----------------------|------------------------------------|
| 特性 | 第一层: Slot 内前缀复用 | 第二层:跨 Slot RAM 存档 | 第三层:上下文检查点 |
| 存储位置 | GPU 显存 | 系统内存 | 系统内存 |
| 控制参数 | 自动启用 | --cache‑ram | --ctx‑checkpoints, --cache‑ram |
| 主要场景 | 多轮对话 | 会话数 > slot 数 | 长上下文 / 上下文移位 |
| 存储内容 | 完整 KV 前缀 | 完整 KV 状态 | 部分不可重建状态 |

需要详细解释的第三层是llama.cpp的上下文检查点系统,用于处理长上下文和上下文移场景。它通过定期保存KV状态的"部分快照"来避免完全重新处理长提示。

第三层检查点参数

|----------------------------------|-----------------|----------|
| 参数 | 作用 | 默认值 |
| --ctx-checkpoints / -ctxcp | 每个 slot 的最大检查点数 | 32 |
| --checkpoint-min-step / -cms | 创建检查点的最小令牌间隔 | 8192 |
| --cache-ram / -cram | 检查点使用的 RAM 预算 | 8192 MiB |

实际应用场景

  1. 长上下文处理:当上下文超过模型限制时,通过检查点移,避免完全重新处理
  2. 代理工具调用:在本地编码代理等场景中,工具调用后恢复状态,大幅减少冗余计算
  3. 混合注意力模型:对Qwen3.5等具有滑动窗口注意力的模型特别有用

第三层是缓存"不可重建的中间状态"

在llama.cpp的上下文检查点系统中,"不可重建的中间状态" 是指那些无法通过简单截断KV cache来恢复的模型内部状态

|----------|---------------------------------|------------------------------------------|
| 状态类型 | 完整注意力 KV Cache | 不可重建的中间状态 |
| 存储形式 | 每个 token 都有独立的 Key/Value 对 | 滑动窗口注意力 (SWA) 的 KV 或 递归层 (如 Mamba) 的运行状态 |
| 能否截断恢复 | 可以,llama_memory_seq_rm () 能任意删除 | 不能,一旦滑动或覆盖,旧数据就丢失 |
| 检查点是否存储 | 不存储(仍在 slot 中,可截断恢复) | 必须存储(这是检查点唯一保存的内容) |

两种典型不可重建状态

A. 滑动窗口注意力(SWA)的KV Cache

对于采用SWA的模型(如Qwen3.6、Mistral),每个注意力层只维护一个固定大小的"窗口",窗口外的token的KV会被丢弃。

SWA(滑动窗口注意力)机制

  • 不是"压缩+新内容保留" ,而是维护固定大小的窗口
  • 当处理新Token时,窗口外的最旧Token的KV会被永久丢弃
  • 窗口内的Token可能包含新Token和部分旧Token

B. 递归层的运行状态(如Mamba、RWKV)

对于Mamba、RWKV等状态空间模型 ,它们不存储每个token的KV ,而是维护一个单一的"运行状态" ,这个状态在每个token处理时都会被原地更新。

递归模型(如Mamba)机制

  • 没有独立的KV对 ,而是维护一个单一的运行状态
  • 这个状态在每个Token处理时都会被原地更新(覆盖)

检查点设计的主要目标。当处理一个长prompt后,后续请求如果与之前的对话有共同前缀,server会尝试找到匹配的检查点,恢复到某个中间状态,只计算新增的token。

典型场景:多轮对话中,用户发送新请求(包含之前所有对话历史+新问题)。如果没有检查点,server需要重新处理整个对话历史;有了检查点,可以恢复到之前某个状态,只处理新问题部分。

相关推荐
yyk333244 小时前
OpenCV中的LBPH人脸识别
人工智能·opencv·计算机视觉
天天代码码天天4 小时前
把 PP-OCR 塞进一个纯 C Runtime:从 Tiny 到 Medium,再到单文件 HTML OCR
人工智能
xsd202411184 小时前
Stremio-WebStremio:开源流媒体自由聚合平台
人工智能
Aloudata4 小时前
语义治理 vs 知识治理:AI 数据分析需要业务知识库还是可执行语义层
大数据·人工智能·数据分析·data agent·语义层
阿黎梨梨4 小时前
LangGraph 核心机制:从状态管理到人机协同
人工智能·langchain
小兵金林4 小时前
初识 Transformer
人工智能
小白的后端世界4 小时前
数据分析基础学习
人工智能·学习·数据分析
wangfpp4 小时前
手写 ReAct 循环:搞懂 Agent 工具调用
人工智能
leluckys4 小时前
AI-DeepSeek使用与提示词工程
人工智能
dunge20264 小时前
2026年9月9日|ChatGPT Pro + Codex:GPT‑6 Astra 自动测试与代码审查
人工智能·gpt·chatgpt