Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 237 additions & 0 deletions T2-1-2_伊里斯_赛题报告.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# T2-1-2 服务能力优化报告

## 摘要

本提交针对 InfiniLM 在 RTX 4090 24GB 上运行 Llama-3.1-8B-Instruct 的高并发与长文本服务场景进行调度优化。本组成员在调测过程中发现,原始调度在部分长输入或高并发 prefill/decode 交叠场景下会触发 `cudaMalloc failed`,使 RankWorker 停止并导致服务 unhealthy。改动将 prefill 与 decode 的 batch 上限拆分,并对真正的长输入实施保守保护,在保持小规模请求性能无明显下降的同时,提升高并发短输出场景吞吐,并消除已复现的长输入服务失效。

受当前可用硬件与模型存储条件限制,本次提交仅完成 8B 模型的完整测试矩阵与优化验证,未对 70B 模型给出实测结果。本文所有性能数据、截图和结论均以 8B 模型为准;调度策略本身不依赖 8B 特有结构,理论上可迁移到 70B 服务,但仍需要在具备足够显存、模型存储和多卡通信条件的环境中单独验证。

## 环境与复现

- InfiniLM commit: `7c1efb5`
- 模型: `Llama-3.1-8B-Instruct`
- GPU: NVIDIA RTX 4090 24GB, TP=1
- KV cache: `--num-blocks 64`, `--block-size 256`
- 测试工具:`vllm bench serve`
- 测试范围:官方 8B 测试矩阵,共 30 个点

| 并发数 | 输入长度(tokens) | 输出长度(tokens) |
| -------- | ------------- | --------------- |
| 1, 4 | 32, 256, 4096 | 256, 1024, 4096 |
| 16, 64 | 32, 256 | 256, 1024, 4096 |

- 说明:本节参数为本组在 RTX 4090 24GB 单卡环境下针对 8B 测试矩阵调测得到的稳定配置,并不宣称为所有硬件和模型上的全局最优配置。

关键参数选择:

- `--num-blocks 64`:在 4090 24GB 上可稳定运行完整 8B 矩阵。
- `--max-batch-size 64`:保留高并发请求的 admission 上限,实际 prefill/decode 批次由调度器环境变量进一步约束。
- `INFINILM_PREFILL_BATCH_CAP=8`:限制 prefill 阶段瞬时显存峰值。
- `INFINILM_DECODE_BATCH_CAP=24`:允许 decode 阶段使用更大的安全批次,提高输出吞吐。
- `INFINILM_LONG_CONTEXT_THRESHOLD=4096` 与 `INFINILM_LONG_CONTEXT_MAX_BATCH=1`:对真正长输入请求进行保守保护。
- `INFINILM_DECODE_FIRST_WHEN_RUNNING=1`:已有 decode 请求运行时延后新的长输入 prefill,避免长 prefill 与 decode 叠加触发 OOM。

反例参数:

- `num-blocks=80/96`:服务可启动,但在长输入 forward 时触发 OOM。
- `block-size=128`:触发 paged-attention/graph 编译失败。
- 仅降低全局 `max-batch-size` 到 2 或 3:仍不能稳定解决长输入卡点,说明问题不只是请求数,而是 prefill/decode 阶段显存压力不同。

复现启动命令:

```bash
export INFINILM_LONG_CONTEXT_THRESHOLD=4096
export INFINILM_LONG_CONTEXT_MAX_BATCH=1
unset INFINILM_LONG_CONTEXT_USE_TOTAL_LEN
export INFINILM_DECODE_FIRST_WHEN_RUNNING=1
export INFINILM_PREFILL_BATCH_CAP=8
export INFINILM_DECODE_BATCH_CAP=24

python python/infinilm/server/inference_server.py \
--device nvidia --model=/root/autodl-tmp/models/Llama-3.1-8B-Instruct \
--port 8102 --tp 1 --max-new-tokens 4096 --num-blocks 64 \
--max-batch-size 64 --enable-graph --enable-paged-attn \
--attn flash-attn --ignore-eos
```

## 优化内容

修改文件:`python/infinilm/llm/scheduler.py`。

1. 增加 `INFINILM_PREFILL_BATCH_CAP` 与 `INFINILM_DECODE_BATCH_CAP`:允许 prefill 保持保守批次以控制瞬时显存,同时让 decode 使用更大的安全批次以提升输出吞吐。
2. 增加长输入判定与 `INFINILM_LONG_CONTEXT_MAX_BATCH`:默认只将 `prompt_len >= 4096` 的请求视为长上下文,避免把短输入、长输出误串行化。
3. 增加 `INFINILM_DECODE_FIRST_WHEN_RUNNING`:当已有 decode 在运行时,延后新的长输入 prefill,避免长 prefill 与长 decode 重叠导致 OOM。
4. 所有策略均由环境变量控制;默认值保持原始 batch 行为,便于回退和复现。

## 问题与验证

本组在原始配置调测过程中,于以下点复现 `cudaMalloc failed`、`RankWorker stopped` 与 `/health` 异常:

```text
con=4, input=4096, output=1024
con=16, input=256, output=256
```

启用本文调度策略后,长输入卡点稳定完成:

| 场景 | 成功请求 | 输出吞吐 | Mean TTFT | Mean/P99 TPOT | Mean/P99 ITL | 服务状态 |
| ------------------------ | ----: | ----------: | -----------: | ---------------: | ---------------: | ------- |
| con=4, in=4096, out=1024 | 40/40 | 53.89 tok/s | 54604.96 ms | 18.09 / 18.24 ms | 18.07 / 18.90 ms | healthy |
| con=4, in=4096, out=4096 | 40/40 | 54.46 tok/s | 203503.93 ms | 17.33 / 18.61 ms | 18.24 / 19.64 ms | healthy |
| con=16, in=4096, out=256 | 8/8 | 50.75 tok/s | 17291.99 ms | 22.60 / 36.95 ms | 22.51 / 36.27 ms | healthy |

高并发分相位限批的消融结果:

| 场景 | 策略 | 成功请求 | 输出 tok/s | 总 tok/s | Mean/P99 TTFT ms | Mean/P99 TPOT ms | Mean/P99 ITL ms | 结果 |
|---|---|---:|---:|---:|---:|---:|---:|---|
| con=16, in=256, out=256 | 静态 max-batch=8 | 80/80 | 407.30 | 814.65 | 437.84 / 619.32 | 37.70 / 39.20 | 37.54 / 39.61 | healthy |
| con=16, in=256, out=256 | prefill=8, decode=24 | 80/80 | 726.03 | 1445.77 | 468.25 / 648.21 | 20.26 / 21.79 | 20.20 / 22.70 | healthy |
| con=64, in=256, out=256 | 静态 max-batch=8 | 128/128 | 376.64 | 753.37 | 21498.23 / 69212.98 | 51.69 / 53.70 | 51.50 / 61.84 | healthy |
| con=64, in=256, out=256 | prefill=8, decode=24 | 128/128 | 725.92 | 1449.00 | 10944.62 / 25269.96 | 24.19 / 25.64 | 24.09 / 27.34 | healthy |
| con=64, in=32, out=256 | prefill=8, decode=24 | 64/64 | 1052.16 | 1180.10 | 4299.80 / 8402.76 | 29.31 / 30.72 | 29.20 / 47.44 | healthy |

## 边界与解释

`output=4096` 的高并发受 KV 容量而非 decode kernel 限制。以 `input=256, output=4096` 为例,每个请求约需要 `ceil((256+4096)/256)=17` 个 block;64 个 block 只能容纳约 3 个完整请求。因此 `con=16` 测得 137.23 tok/s、Mean TPOT 19.61ms、Mean TTFT 176.76s。该现象是安全 admission 导致的排队,服务保持 healthy。

不采用“只少预留几个 block”的激进策略,因为没有 KV swap/preemption 时,请求会在生成中途耗尽 block,风险从排队变成失败。

## 8B 测试矩阵运行结果

按照赛题给出的 8B 测试矩阵,本组共完成 30 个测试点,所有测试点均成功返回,服务保持 healthy。其中 `con=16/64, in=256, out=256/1024` 采用参数扫描阶段中与官方矩阵完全相同配置的成功结果,其余高并发点来自最终续跑目录。

| con | input | output | success | 输出 tok/s | 总 tok/s | Mean/P99 TTFT ms | Mean/P99 TPOT ms | Mean/P99 ITL ms |
|---:|---:|---:|---:|---:|---:|---:|---:|---:|
| 1 | 32 | 256 | 20/20 | 56.86 | 63.77 | 32.04 / 35.86 | 17.53 / 17.59 | 17.46 / 18.12 |
| 1 | 32 | 1024 | 20/20 | 56.42 | 58.13 | 33.57 / 37.35 | 17.71 / 17.78 | 17.70 / 18.34 |
| 1 | 32 | 4096 | 20/20 | 56.19 | 56.62 | 36.27 / 39.52 | 17.79 / 17.89 | 17.79 / 18.52 |
| 1 | 256 | 256 | 20/20 | 55.84 | 111.68 | 59.00 / 65.34 | 17.75 / 17.83 | 17.68 / 18.29 |
| 1 | 256 | 1024 | 20/20 | 56.34 | 70.40 | 62.17 / 70.27 | 17.71 / 18.07 | 17.68 / 18.26 |
| 1 | 256 | 4096 | 20/20 | 56.07 | 59.56 | 72.66 / 95.72 | 17.82 / 17.86 | 17.84 / 19.03 |
| 1 | 4096 | 256 | 20/20 | 49.95 | 852.13 | 530.22 / 571.01 | 18.01 / 18.08 | 17.94 / 18.94 |
| 1 | 4096 | 1024 | 20/20 | 53.96 | 269.84 | 540.27 / 581.55 | 18.02 / 18.11 | 18.01 / 19.12 |
| 1 | 4096 | 4096 | 20/20 | 54.40 | 108.78 | 552.19 / 587.24 | 18.25 / 18.30 | 18.25 / 19.30 |
| 4 | 32 | 256 | 40/40 | 219.68 | 246.37 | 68.45 / 83.37 | 18.01 / 18.30 | 17.94 / 19.76 |
| 4 | 32 | 1024 | 40/40 | 218.34 | 224.96 | 71.65 / 85.85 | 18.27 / 18.37 | 18.31 / 20.60 |
| 4 | 32 | 4096 | 40/40 | 147.13 | 148.25 | 26035.76 / 81451.53 | 19.49 / 19.92 | 19.49 / 21.63 |
| 4 | 256 | 256 | 40/40 | 211.18 | 422.36 | 149.97 / 188.61 | 18.42 / 18.83 | 18.35 / 19.56 |
| 4 | 256 | 1024 | 40/40 | 212.85 | 265.91 | 155.94 / 196.74 | 18.66 / 18.79 | 18.65 / 19.79 |
| 4 | 256 | 4096 | 40/40 | 142.62 | 151.50 | 26983.21 / 83198.15 | 20.12 / 20.30 | 20.11 / 21.85 |
| 4 | 4096 | 256 | 40/40 | 106.39 | 1814.25 | 2939.61 / 7078.04 | 24.76 / 25.73 | 24.66 / 24.14 |
| 4 | 4096 | 1024 | 40/40 | 53.89 | 269.49 | 54604.96 / 462816.24 | 18.09 / 18.24 | 18.07 / 18.90 |
| 4 | 4096 | 4096 | 40/40 | 54.46 | 111.78 | 203503.93 / 1000116.28 | 17.33 / 18.61 | 18.24 / 19.64 |
| 16 | 32 | 256 | 16/16 | 798.81 | 895.94 | 174.00 / 237.02 | 19.40 / 19.79 | 19.32 / 23.38 |
| 16 | 32 | 1024 | 16/16 | 414.56 | 427.12 | 5312.69 / 20810.77 | 19.69 / 20.24 | 19.68 / 23.08 |
| 16 | 32 | 4096 | 16/16 | 138.96 | 140.02 | 174643.55 / 386805.00 | 19.36 / 19.54 | 19.36 / 21.08 |
| 16 | 256 | 256 | 80/80 | 726.03 | 1445.77 | 468.25 / 648.21 | 20.26 / 21.79 | 20.20 / 22.70 |
| 16 | 256 | 1024 | 80/80 | 483.11 | 602.73 | 11469.24 / 21660.05 | 20.43 / 20.83 | 20.42 / 23.17 |
| 16 | 256 | 4096 | 16/16 | 137.23 | 145.77 | 176758.32 / 392478.99 | 19.61 / 19.88 | 19.61 / 21.67 |
| 64 | 32 | 256 | 64/64 | 1052.16 | 1180.10 | 4299.80 / 8402.76 | 29.31 / 30.72 | 29.20 / 47.44 |
| 64 | 32 | 1024 | 64/64 | 530.96 | 547.03 | 45873.64 / 104608.76 | 20.20 / 20.45 | 20.18 / 23.19 |
| 64 | 32 | 4096 | 64/64 | 153.98 | 155.97 | 682827.66 / 1000204.93 | 11.86 / 19.67 | 19.48 / 21.49 |
| 64 | 256 | 256 | 128/128 | 725.92 | 1449.00 | 10944.62 / 25269.96 | 24.19 / 25.64 | 24.09 / 27.34 |
| 64 | 256 | 1024 | 128/128 | 475.28 | 593.76 | 86502.94 / 207693.01 | 20.44 / 20.78 | 20.43 / 23.28 |
| 64 | 256 | 4096 | 64/64 | 152.06 | 168.37 | 686751.99 / 1000352.92 | 12.07 / 19.81 | 19.72 / 21.50 |

结果上,短输出场景受益最明显:`con=64, in=32, out=256` 输出吞吐达到 1052.16 tok/s,`con=64, in=256, out=256` 达到 725.92 tok/s。长输出场景的 TPOT/ITL 仍保持在约 19-21ms,但 TTFT 会随 KV 容量排队显著升高,这是 24GB 单卡与 64 个 KV block 下的容量边界。

## 单并发基线与 decode 稳定性

为确认问题并非来自单请求 decode 性能,本组首先记录了 8B 模型在单并发下的基线表现。以下结果均使用 Flash Attention 与 Paged Attention,每个测试点 20 个请求。

| con=1 | input | output | 输出 tok/s | 总 tok/s | Mean TTFT ms | Mean/P99 TPOT ms | Mean/P99 ITL ms |
| ----: | ----: | -----: | -------: | ------: | -----------: | ----------------: | ---------------: |
| 1 | 32 | 256 | 56.86 | 63.77 | 32.04 | 17.53 / 17.59 | 17.46 / 18.12 |
| 1 | 32 | 1024 | 56.42 | 58.13 | 33.57 | 17.71 / 17.78 | 17.70 / 18.34 |
| 1 | 32 | 4096 | 56.19 | 56.62 | 36.27 | 17.79 / 17.89 | 17.79 / 18.52 |
| 1 | 256 | 256 | 55.84 | 111.68 | 59.00 | 17.75 / 17.83 | 17.68 / 18.29 |
| 1 | 256 | 1024 | 56.34 | 70.40 | 62.17 | 17.71 / 18.07 | 17.68 / 18.26 |
| 1 | 256 | 4096 | 56.07 | 59.56 | 72.66 | 17.82 / 17.86 | 17.84 / 19.03 |
| 1 | 4096 | 256 | 49.95 | 852.13 | 530.22 | 18.01 / 18.08 | 17.94 / 18.94 |
| 1 | 4096 | 1024 | 53.96 | 269.84 | 540.27 | 18.02 / 18.11 | 18.01 / 19.12 |
| 1 | 4096 | 4096 | 54.40 | 108.78 | 552.19 | 18.25 / 18.30 | 18.25 / 19.30 |

结论:单流 decode 始终约 56 tok/s、TPOT 约 18ms;4096 输入主要增加 prefill/TTFT,而未破坏 decode 路径。这为后续将问题定位到“并发调度时的瞬时显存峰值”提供了对照。

## 并发基线与故障复现

| con=4 | input | output | 输出 tok/s | Mean TTFT ms | Mean/P99 TPOT ms | Mean/P99 ITL ms | 现象 |
|---:|---:|---:|---:|---:|---:|---:|---|
| 4 | 32 | 256 | 219.68 | 68.45 | 18.01 / 18.30 | 17.94 / 19.76 | 正常 |
| 4 | 32 | 1024 | 218.34 | 71.65 | 18.27 / 18.37 | 18.31 / 20.60 | 正常 |
| 4 | 32 | 4096 | 147.13 | 26035.76 | 19.49 / 19.92 | 19.49 / 21.63 | KV 排队 |
| 4 | 256 | 256 | 211.18 | 149.97 | 18.42 / 18.83 | 18.35 / 19.56 | 正常 |
| 4 | 256 | 1024 | 212.85 | 155.94 | 18.66 / 18.79 | 18.65 / 19.79 | 正常 |
| 4 | 256 | 4096 | 142.62 | 26983.21 | 20.12 / 20.30 | 20.11 / 21.85 | KV 排队 |
| 4 | 4096 | 256 | 106.39 | 2939.61 | 24.76 / 25.73 | 24.66 / 24.14 | 长输入干扰 decode |

在进一步调测中,`con=4, in=4096, out=1024` 于原始实现下复现 `cudaMalloc failed`、`RankWorker stopped` 和服务 unhealthy;`con=16, in=256, out=256` 也复现相同类型的 failure。这说明仅按请求数准入并不足以覆盖长 prefill 或高并发 prefill/decode 交叠时的临时显存开销。

## 参数扫描:排除不可靠路径

| 尝试 | 结果 | 决策 |
|---|---|---|
| `num-blocks=96` / block 256 | 空载约 22587 MiB,首个长输入 forward OOM | 超出 24GB 余量,不采用 |
| `num-blocks=80` / block 256 | 长输入 forward OOM | 不采用 |
| `num-blocks=128` / block 128 | paged attention/graph 编译失败 | 不采用 |
| max batch=3 或 2 | 长输入仍 OOM/unhealthy | 仅全局降 batch 不足 |

这组反例说明,问题不能通过单纯扩大 KV cache 或全局降低 batch size 稳定解决。最终方案需要区分 prefill 与 decode 阶段的显存压力,为两类阶段设置不同的安全上限。

## 优化迭代与消融

### 长输入保护的两次迭代

第一版策略仅限制同一轮 schedule 中的长输入请求数量,但新的长输入 prefill 仍可能与已经在运行队列中的 decode 请求交叠。在 `con=4, in=4096, out=1024` 场景下,该版本仍会触发服务 unhealthy。

第二版加入 decode-first 保护:当 running queue 非空时,新的长输入 prefill 会被延后调度,优先让已有 decode 请求继续推进。该策略避免了长输入 prefill 与 decode 阶段叠加造成的瞬时显存峰值,`con=4, in=4096, out=1024` 最终稳定完成 40/40 请求。

随后,本组将 long-context 判定从“总长度达到 4096”收窄为默认仅 `prompt_len >= 4096`。这样可以只保护真正的长输入请求,避免把短输入、长输出请求误判为长上下文并强制串行化。以 `con=4, in=32, out=4096` 为例,该调整使输出吞吐从 108.46 提升到 137.01 tok/s,Median TTFT 从 73100ms 降至 66ms,同时长输入卡点仍保持稳定。

长输入保护解决了服务稳定性问题,但高并发短输出场景仍会受到全局 batch 上限限制。为此,本组进一步将 prefill 与 decode 的 batch cap 拆分,使 prefill 保持保守批次以控制显存峰值,同时让 decode 使用更大的安全批次提升输出吞吐。

### 分相位 batch cap

| 场景 | 策略 | 输出 tok/s | 总 tok/s | Mean/P99 TTFT ms | Mean/P99 TPOT ms | Mean/P99 ITL ms |
|---|---|---:|---:|---:|---:|---:|
| con=16, in=256, out=256 | max batch=8 | 407.30 | 814.65 | 437.84 / 619.32 | 37.70 / 39.20 | 37.54 / 39.61 |
| con=16, in=256, out=256 | prefill=8/decode=16 | 724.84 | 1449.68 | 468.86 / 648.64 | 20.30 / 21.82 | 20.23 / 22.91 |
| con=16, in=256, out=256 | prefill=8/decode=24 | 726.03 | 1445.77 | 468.25 / 648.21 | 20.26 / 21.79 | 20.20 / 22.70 |
| con=16, in=256, out=1024 | max batch=8 | 399.02 | 497.97 | 13727.04 / 51497.49 | 24.91 / 25.38 | 24.90 / 41.97 |
| con=16, in=256, out=1024 | prefill=8/decode=16 | 483.19 | 603.04 | 11460.90 / 21678.92 | 20.43 / 20.78 | 20.41 / 23.36 |
| con=16, in=256, out=1024 | prefill=8/decode=24 | 483.11 | 602.73 | 11469.24 / 21660.05 | 20.43 / 20.83 | 20.42 / 23.17 |
| con=64, in=256, out=256 | max batch=8 | 376.64 | 753.37 | 21498.23 / 69212.98 | 51.69 / 53.70 | 51.50 / 61.84 |
| con=64, in=256, out=256 | prefill=8/decode=16 | 631.06 | 1261.17 | 12516.38 / 31741.32 | 29.07 / 30.60 | 28.96 / 46.19 |
| con=64, in=256, out=256 | prefill=8/decode=24 | 725.92 | 1449.00 | 10944.62 / 25269.96 | 24.19 / 25.64 | 24.09 / 27.34 |

从消融结果看,`decode=24` 对 con=16 场景收益有限,但在 con=64 短输出场景中,将输出吞吐从 631.06 提升到 725.92 tok/s,并将 Mean TPOT 从 29.07ms 降至 24.19ms、Mean ITL 从 28.96ms 降至 24.09ms。因此,本次 8B 提交采用 `prefill=8/decode=24` 作为最终配置。

## 调度策略说明

waiting queue:
- prefill 阶段最多调度 prefill_batch_cap 个请求
- 若请求 prompt_len >= 4096,且已有 decode 请求正在运行,则延后该 prefill
- 同一 batch 中 long-context 请求数不超过 long_context_max_batch

running queue:
- decode 阶段最多调度 decode_batch_cap 个请求
- 同样遵守 long-context batch 上限

该策略将稳定性保护集中在真正的长输入 prefill 上,同时避免短输入、长输出请求被不必要地串行化。对于普通短/中输入请求,decode 阶段可以使用更大的 batch cap 提升吞吐;对于真正长输入请求,则避免其 prefill 与正在执行的 decode 阶段叠加到不安全的瞬时显存峰值。


## 结果文件与审计

- 原始详细日志:`/root/autodl-tmp/bench-results-official-8b/*.log`
- vLLM 原始 JSON:`/root/autodl-tmp/bench-results-official-8b/*.json`
- 参数扫描结果:`/root/autodl-tmp/bench-results-param-scan/`
- PR 测试截图:`doc_extern/pr_screenshot.png`
- 实验全过程记录:`doc_extern/实验过程记录与卡点分析.md`

最后 6 个官方矩阵点已由 `doc_extern/run_remaining_official_8b.sh` 顺序完成;所有点均有日志或 JSON 可追溯。

PR内容包括:

- 修改文件:`python/infinilm/llm/scheduler.py`
- 赛题报告:`T2-1-2_伊里斯_赛题报告.md`

Loading
Loading