This commit is contained in:
FlintyLemming
2026-06-07 20:02:45 +08:00
parent 11b1ebd676
commit e00598734d
@@ -0,0 +1,392 @@
+++
author = "FlintyLemming"
title = "GLM-5.1 Coding 场景部署优化: HiCache 配置指南与场景压测"
slug = "256779ab64688063b3c9000c80fc01c6"
date = "2026-06-07"
description = ""
categories = ["AI"]
tags = ["SGLang", "GLM", "HiCache"]
image = "https://assets.mitsea.cn/blog/posts/2022/08/Apple%20Transit%20%E9%87%91%E9%99%B5%E9%80%9A%E4%BA%A4%E9%80%9A%E5%8D%A1%E5%BC%80%E5%8F%91%E7%A5%A8/title.avif"
+++
## 背景
[SGLang HiCache](https://www.lmsys.org/blog/2025-09-10-sglang-hicache/) 在去年 9 月就已发布。它把 KV cache 分层到 GPU HBM 与主机内存(可选再下沉到磁盘),目标是在长上下文、多会话复用场景下,用更大的有效 cache 容量换更低的显存压力和更好的 cache 命中率。
但 [SGLang 官方 GLM-5.1 部署指南](https://docs.sglang.io/cookbook/autoregressive/GLM/GLM-5.1) 只写了默认的 EAGLE 投机解码(MTP)启动参数,**没有提到 HiCache 的开启方式**。如果你把 GLM-5.1 当 coding agent 后端用——多 repo 长上下文、持续 decode、周期性大 prefill——官方 compose 未必是最优选择。
本文记录我在 **8× NVIDIA H200、TP=8** 上部署 GLM-5.1-FP8 时启用 HiCache 的配置,以及与默认 MTP 方案的对比压测结果。文章由 AI 根据测试脚本和结果自动生成。
---
## HiCache 做了什么
简要概括 SGLang HiCache 的三层结构:
| 层级 | 介质 | 作用 |
|------|------|------|
| L1 | GPU HBM | 热 KV,低延迟 |
| L2 | Host DRAM | 溢出缓存,容量远大于显存 |
| L3 | 磁盘(可选) | 更大容量,适合冷数据 |
对 coding agent 这类 workload,典型收益路径是:
1. **多 repo / 多会话**:不同上下文的 KV 可以在 host 侧保留,减少重复 prefill。
2. **长上下文**100k token 级别的 prompt 不可能全部常驻 GPUHiCache 把冷 KV 换出到 host,需要时再 `load_back`
3. **与 MTP 的取舍**HiCache 和 EAGLE 投机解码争用显存与调度资源,实践中往往不能同时开满。
---
## 部署配置
### 硬件与镜像
- GPU8× NVIDIA H200
- 镜像:`lmsysorg/sglang:latest`
- 模型:`/mnt/extend/models/llm/ZhipuAI/GLM-5.1-FP8`FP8 量化)
- 并行:`--tp-size 8`
- 端口映射:`30001:8000`
### 官方默认配置(MTP,无 HiCache
习惯使用 docker 部署以对齐官方环境使用的 CUDA 版本
文件:`compose.yaml`
```yaml
services:
sglang-glm-51:
image: lmsysorg/sglang:latest
restart: unless-stopped
container_name: sglang-glm-51-h200
init: true
stop_grace_period: 10m
runtime: nvidia
ports:
- "30001:8000"
volumes:
- /mnt/extend/models/llm/ZhipuAI/GLM-5.1-FP8:/models/GLM-5.1:ro
- ./default/cache/deep_gemm:/root/.cache/deep_gemm
- ./default/cache/triton:/root/.triton
- ./default/cache/flashinfer:/root/.cache/flashinfer
- ./default/cache/tvm-ffi:/root/.cache/tvm-ffi
- ./default/cache/sglang:/root/.cache/sglang
- ./default/cache/huggingface:/root/.cache/huggingface
environment:
- NVIDIA_VISIBLE_DEVICES=all
- SGLANG_ENABLE_SPEC_V2=1
ipc: host
shm_size: "128g"
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 8
capabilities: [gpu]
command:
- "python3"
- "-m"
- "sglang.launch_server"
- "--model-path"
- "/models/GLM-5.1"
- "--tp-size"
- "8"
- "--served-model-name"
- "glm-5.1"
- "--reasoning-parser"
- "glm45"
- "--tool-call-parser"
- "glm47"
- "--speculative-algorithm"
- "EAGLE"
- "--speculative-num-steps"
- "3"
- "--speculative-eagle-topk"
- "1"
- "--speculative-num-draft-tokens"
- "4"
- "--host"
- "0.0.0.0"
- "--port"
- "8000"
- "--enable-metrics"
```
特点:开启 **EAGLE 投机解码**`SGLANG_ENABLE_SPEC_V2=1`),无分层 cache。
### HiCache compose
文件:`compose-hicache.yaml`
```yaml
services:
sglang-glm-51-hicache:
image: lmsysorg/sglang:latest
container_name: sglang-glm-51-hicache-h200
init: true
stop_grace_period: 10m
runtime: nvidia
ports:
- "30001:8000"
volumes:
- /mnt/extend/models/llm/ZhipuAI/GLM-5.1-FP8:/models/GLM-5.1:ro
- ./default/cache/deep_gemm:/root/.cache/deep_gemm
- ./default/cache/triton:/root/.triton
- ./default/cache/flashinfer:/root/.cache/flashinfer
- ./default/cache/tvm-ffi:/root/.cache/tvm-ffi
- ./default/cache/sglang:/root/.cache/sglang
- ./default/cache/huggingface:/root/.cache/huggingface
environment:
- NVIDIA_VISIBLE_DEVICES=all
ipc: host
shm_size: "128g"
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 8
capabilities: [gpu]
command:
- "python3"
- "-m"
- "sglang.launch_server"
- "--model-path"
- "/models/GLM-5.1"
- "--tp-size"
- "8"
- "--served-model-name"
- "glm-5.1"
- "--reasoning-parser"
- "glm45"
- "--tool-call-parser"
- "glm47"
- "--host"
- "0.0.0.0"
- "--port"
- "8000"
- "--mem-fraction-static"
- "0.85"
- "--enable-metrics"
- "--enable-hierarchical-cache"
- "--hicache-size"
- "200"
- "--hicache-io-backend"
- "direct"
- "--hicache-mem-layout"
- "page_first"
- "--hicache-write-policy"
- "write_through"
- "--watchdog-timeout"
- "1800"
```
### 关键差异对照
| 项目 | `compose.yaml`MTP | `compose-hicache.yaml`HiCache |
|------|----------------------|-----------------------------------|
| 投机解码 | EAGLE3 steps4 draft tokens | **关闭**(去掉全部 `--speculative-*` |
| 环境变量 | `SGLANG_ENABLE_SPEC_V2=1` | 无 |
| 分层 cache | 无 | `--enable-hierarchical-cache` |
| Host cache 容量 | — | `--hicache-size 200`GB |
| IO 后端 | — | `direct` |
| 内存布局 | — | `page_first` |
| 写策略 | — | `write_through` |
| 静态显存占比 | 默认 | `--mem-fraction-static 0.85` |
| Watchdog | 默认 | `1800` 秒(长上下文 prefill 不易误杀) |
启动方式:
```bash
# HiCache 方案
docker compose -f compose-hicache.yaml up -d --remove-orphans
# 回退到 MTP 方案
docker compose -f compose.yaml up -d --remove-orphans
```
两套 compose 共用同一端口 `30001`**不同时 up**。
---
## 压测脚本
纯 Python 标准库实现,不依赖额外压测框架。[测试脚本](https://gist.github.com/FlintyLemming/23602cd0b8584dee903a50722c42ea0d)
### 1. Mixed workload 基准:`coding_agent_mixed_benchmark.py`
模拟 coding agent 的真实混合负载:
- **4 个约 100k token 的 repo context**(合成代码仓库文本)
- **3 条持续 decode** worker,每条最多输出 2048 token
-**30s** 注入 1 条 100k input / 64 output 的 prefill spike
-**45s** 对已有 context 做 **revisit**(模拟切回旧 repo
- 持续 **600s**,采集客户端流式指标与服务端 Prometheus metrics
内置两个 profile,通过 `--profiles` 切换:
| Profile key | Compose | 说明 |
|-------------|---------|------|
| `hicache` | `compose-hicache.yaml` | HiCache 开启,MTP 关闭 |
| `mtp` | `compose.yaml` | EAGLE MTP 开启,HiCache 关闭 |
示例命令:
```bash
cd /mnt/extend/models/llm/ZhipuAI/GLM-5.1-FP8/stress-test
# 只测 HiCache(脚本会自动 docker compose up/down
python3 coding_agent_mixed_benchmark.py \
--profiles hicache \
--duration 600 \
--decode-workers 3 \
--repo-count 4
# 对比两组(先跑 hicache 再跑 mtp,自动切换 compose
python3 coding_agent_mixed_benchmark.py \
--profiles hicache,mtp \
--duration 600
# 服务已手动启动时跳过 compose 管理
python3 coding_agent_mixed_benchmark.py \
--profiles hicache \
--reuse-running \
--keep-running
```
输出:`stress-test/reports/` 下的 `.md` 报告、`.json` 原始数据、`.svg` 图表。
### 2. 并发容量基准:`coding_agent_capacity_benchmark.py`
[测试脚本](https://gist.github.com/FlintyLemming/88f4ece905acbdb358cbdc4037b5f96e)
回答「能同时跑几个 coding agent」:
- **8 个约 100k token context**
- 并发档位:1 / 2 / 4 / 8 / 12(可自定义)
- 每档持续 **240s**decode 输出上限 **1024 token**
- **mixed 模式**:每档内额外每 25s 注入 1 条 100k prefill 短输出请求
- 可用判定:失败率 ≤ 5%、单请求 output tok/s P50 ≥ 4、TTFT P95 ≤ 180s、max chunk gap P95 ≤ 8s
```bash
python3 coding_agent_capacity_benchmark.py \
--profiles hicache,mtp \
--modes mixed \
--concurrency-levels 1,2,4,8,12 \
--reuse-running
```
---
## 测试过程
测试日期:**2026-06-07**。
### 环境准备
1. 确认 8× H200 可见,`docker compose` 可用。
2. 模型权重挂载到 `/models/GLM-5.1`
3. 启动目标 profile 的 compose,等待 `/v1/models` 可访问(脚本默认最多等 1800s)。
4. 可选:配合 Grafana 观察 `sglang_gen_throughput``sglang_num_queue_reqs` 等指标(脚本也会直接拉 `/metrics`)。
### 测试一:10 分钟 mixed workload
对 HiCache 与 MTP 各跑一轮 600s mixed 测试,口径:
- **客户端 output tok/s** = `completion_tokens / 实际完成 wall time`
- **Grafana 同口径** = Prometheus `sglang_gen_throughput` 采样均值
- **max chunk gap** = 流式输出中相邻 token chunk 的最大间隔(反映「卡住」体感)
- **HiCache 专属** = `cached_host``evicted``load_back` counter 增量
### 测试二:mixed 多并发容量
在 mixed 背景下扫描 1→12 并发,找出满足可用阈值的最大并发档位。
---
## 测试结果
### Mixed 10 分钟:核心指标
| Profile | Client output tok/s | Grafana avg tok/s | Queue avg/P95 | TTFT P50/P95 | Max chunk gap P50/P95 |
|---------|--------------------:|------------------:|--------------:|-------------:|----------------------:|
| **HiCache / no MTP** | 119.28 | 118.89 | 1.94 / 4.00 | 23.51 / 41.38 | **0.36 / 0.61** |
| MTP / no HiCache | 168.96 | 319.17 | 0.49 / 2.00 | 9.09 / 31.78 | 11.95 / **12.84** |
### HiCache cache 活动(10 分钟测试)
| 指标 | HiCache | MTP |
|------|--------:|----:|
| cached device | 3,456,640 | 4,812,160 |
| cached host | **2,900,096** | 0 |
| evicted | 23,949,824 | 17,915,904 |
| load_back | **23,200,768** | 0 |
| host used after | 885,888 | 0 |
HiCache 路径确实在工作:约 290 万 token 落在 host cache,约 2320 万 token 发生 load_back。
### 流式体验对比
**HiCache** — 输出平稳,几乎无 ≥1s 的 chunk 间隔:
![HiCache mixed 10min](https://assets.mitsea.cn/blog/posts/2026/06/GLM-5.1%20Coding%20%E5%9C%BA%E6%99%AF%E9%83%A8%E7%BD%B2%E4%BC%98%E5%8C%96%EF%BC%9A%20HiCache%20%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97%E4%B8%8E%E5%9C%BA%E6%99%AF%E5%8E%8B%E6%B5%8B/hicache_mixed_10min_20260607.svg)
**MTP** — 平均吞吐更高,但 decode/revisit 请求 **100% 出现 ≥1s 的 chunk gap**,P95 达 12.84s,即「写着写着突然停十几秒」:
![MTP mixed 10min](https://assets.mitsea.cn/blog/posts/2026/06/GLM-5.1%20Coding%20%E5%9C%BA%E6%99%AF%E9%83%A8%E7%BD%B2%E4%BC%98%E5%8C%96%EF%BC%9A%20HiCache%20%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97%E4%B8%8E%E5%9C%BA%E6%99%AF%E5%8E%8B%E6%B5%8B/mtp_mixed_10min_20260607.svg)
### Mixed 多并发容量
| Profile | 最大可用并发 | 可用档最佳 output tok/s | 峰值 output tok/s(超阈值) |
|---------|------------:|------------------------:|----------------------------:|
| **HiCache / no MTP** | **4** | **110.39** @ 并发 4 | 134.70 @ 并发 12 |
| MTP / no HiCache | 1 | 44.80 @ 并发 1 | 94.18 @ 并发 2 |
HiCache 在 mixed 场景下可稳定支撑 **4 路并发** coding agentMTP 在并发 2 就已因 chunk gap 超标被判不可用。
![HiCache 并发容量](https://assets.mitsea.cn/blog/posts/2026/06/GLM-5.1%20Coding%20%E5%9C%BA%E6%99%AF%E9%83%A8%E7%BD%B2%E4%BC%98%E5%8C%96%EF%BC%9A%20HiCache%20%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97%E4%B8%8E%E5%9C%BA%E6%99%AF%E5%8E%8B%E6%B5%8B/coding_agent_capacity_hicache_mixed_reuse_20260607.svg)
![MTP 并发容量](https://assets.mitsea.cn/blog/posts/2026/06/GLM-5.1%20Coding%20%E5%9C%BA%E6%99%AF%E9%83%A8%E7%BD%B2%E4%BC%98%E5%8C%96%EF%BC%9A%20HiCache%20%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97%E4%B8%8E%E5%9C%BA%E6%99%AF%E5%8E%8B%E6%B5%8B/coding_agent_capacity_mtp_mixed_reuse2_20260607.svg)
分档细节(mixed 模式):
| 并发 | HiCache 可用 | HiCache tok/s | HiCache max gap P95 | MTP 可用 | MTP tok/s | MTP max gap P95 |
|-----:|:------------|-------------:|--------------------:|:--------|----------:|----------------:|
| 1 | yes | 52.18 | 0.61s | yes | 44.80 | 0.29s |
| 2 | yes | 98.12 | 0.65s | **no** | 94.18 | 13.61s |
| 4 | yes | 110.39 | 7.72s | no | 55.72 | 26.22s |
| 8 | no | 121.73 | 30.82s | no | 49.28 | 13.54s |
| 12 | no | 134.70 | 52.70s | no | 55.70 | 14.36s |
---
## 分析与结论
### HiCache 带来了什么提升
**coding agent 混合负载**下,HiCache 的优势不在「总吞吐数字」,而在 **体验与容量**
1. **流式输出稳定**max chunk gap P95 从 12.84s 降到 0.61s,消除了 MTP 下频繁的输出停顿。
2. **更高可用并发**:mixed 场景下可用并发从 1 提升到 4,更适合多 agent / 多 tab 同时编码。
3. **host 侧 KV 复用生效**cached host 与 load_back 数据证明分层 cache 在 100k 上下文场景下持续运转。
### 需要接受的代价
1. **平均吞吐更低**Grafana 同口径约 119 vs 319 tok/s。MTP 的投机解码在低干扰时确实更快,但 mixed workload 下高吞吐伴随着严重的 decode 饥饿。
2. **队列与尾部延迟**HiCache 测试 600s 内请求尾部完成到 782.8s,队列 P95 更高;evict/load_back 有 CPU/PCIe 开销。
3. **与 MTP 互斥**:当前配置下二者不能兼得,需按场景选型。
### 选型建议
| 场景 | 推荐 |
|------|------|
| 交互式 coding agent、多 repo 切换、在意输出是否卡顿 | **HiCache**`compose-hicache.yaml` |
| 离线批量生成、单请求、追求峰值 tok/s | MTP(`compose.yaml` |
| 生产监控 | 同时看 `sglang_gen_throughput`、TTFT、queue、客户端 chunk gap,不要只看平均吞吐 |
### 后续可尝试
- 调大 `--hicache-size` 或接入 L3 磁盘层,观察 evict/load_back 比例变化。
- 探索 PD disaggregation,把 prefill 与 decode 拆池,进一步缓解互相干扰。
- 关注 SGLang 新版本是否支持 HiCache + MTP 共存。