阿里云百炼 Qwen 请求 Trace 分析

Reader mode is not fully supported on this site; please use it with caution.

数据来自阿里云百炼上的 Qwen 推理集群,Apache 2.0 开源。
文中统计均由全量扫描四条 JSONL 得到(每条数据约 2 小时)。

这份开源请求数据集的仓库存有2小时 C 端聊天、B 端 API、长思维链、代码助手四类流量。


这是什么数据?

Qwen-Bailian Anonymous Dataset 每条记录有到达时间、输入/输出长度、会话父子关系、请求类型,以及按 16 token 一块 哈希后的 hash_ids。

配套论文为 ATC'25 的 KVCache Cache in the Wild。官方 replay 工具:blitz-serving/trace-replayer。

一条记录参考:

{
  "chat_id": 159,
  "parent_chat_id": 55,
  "timestamp": 61.114,
  "input_length": 521,
  "output_length": 132,
  "type": "text",
  "turn": 2,
  "hash_ids": [1089, 1090, 1091, 6326]
}

timestamp 是相对秒,文件开头为 0。parent_chat_id = -1 表示一轮会话的根。hash_ids 已经是 chat template 之后 的 token 块,replay 时不要再套一遍模板。

点开:四条文件各自对应什么场景

| 文件 | 场景 | 本文简称 |
| --- | --- | --- |
| qwen_traceA_blksz_16.jsonl | C 端交互聊天 | To-C |
| qwen_traceB_blksz_16.jsonl | B 端 API 任务自动化(采集标注 2024-12) | To-B |
| qwen_thinking_blksz_16.jsonl | 长思维链 | Thinking |
| qwen_coder_blksz_16.jsonl | 代码生成 | Coder |

每条都是约两小时的独立切片,**不是**按线上真实 mix 抽的同一段流量。

点开:他们怎么匿名的

1. token 按 16 个一组,做加盐 SipHash-2-4
2. 哈希值再映射成全局整数,切断和原文的对应
3. chat_id 换成顺序整数,对不上账号
4. 墙钟时间全部平移到从 0 开始

所以你能复现「哪些块以前见过」,不能复现「用户说了什么」。


四条 trace

To-C 聊天 To-B API Thinking Coder
请求数 43,058 172,800 10,812 43,011
平均 / 峰值 QPS 6.0 / 9.8 24.0 / 37 1.5 / 10.5 6.0 / 9.2
多轮比例 46% 0% 11% 39%
输入中位数 1,046 574 3,680 4,540
输出中位数 376 39 1,666 469
输入 P99 14,365 6,294 24,973 14,407
输出 P99 1,641 1,006 34,687 5,842
全局 KV 块复用 60% 62% 46% 66%
  • To-B 全是单轮,输出中位 39 token,平均刚好 24 QPS。这是模板化 API。
  • To-C 为 chat。 将近一半多轮,还夹着搜索和图、文件。
  • Coder 吃 prefill。 输入中位 4540,会话最长 54 轮。
  • Thinking 吃长 decode,而且最突发。 均值 1.5 QPS,10 秒峰到 10.5,峰均比约 7。
点开:To-C / To-B 请求类型构成

To-C 与 To-B 的请求类型

To-C:text 73.7%,search 19.0%,image 3.8%,file 3.5%。
To-B:api 87.3%,text 12.7%。B 端几乎没有「你问我答」的会话形态。

记录里没有模型名、用户、语言。能再拆的只有 type。Thinking / Coder 各自只有一个取值。KV 复用下面按该 type 自己的时间序列重算,避免被别的类型带高。

条数 占比 均/峰 QPS 输入中位 / P99 输出中位 / P99 多轮 子集内块复用 相对父请求前缀
To-C / text 31,744 73.7% 4.4 / 7.6 790 / 8,202 371 / 1,714 48% 71% 60%
To-C / search 8,187 19.0% 1.1 / 3.0 5,293 / 16,635 419 / 1,311 46% 51% 90%
To-C / image 1,617 3.8% 0.22 / 0.8 369 / 2,764 69 / 817 8% 85% 63%
To-C / file 1,510 3.5% 0.21 / 1.0 5,712 / 22,390 523 / 2,045 50% 57% 86%
To-B / api 150,936 87.3% 21.0 / 34.1 589 / 5,764 37 / 979 0% 62% —
To-B / text 21,864 12.7% 3.0 / 5.1 479 / 7,695 46 / 1,085 0% 60% —

到达过程

四条都覆盖约 120 分钟。形状完全不像同一种业务。

两小时请求到达率

  1. To-B 的 172800 恰好等于 24 × 7200。 像是截了一段平均 24 QPS 的窗口;但 10 秒峰到 37,五分钟均值在 19–30 之间晃,不是均匀合成到达。
  2. Thinking 开头有尖峰,十分钟后掉到大约 1 QPS。弹性扩缩如果按均值备,会在这段被打穿。
  3. To-C 在两小时里从 ~3 爬到 ~8。 短窗口也会有趋势,不只是平稳噪声。

到达间隔中位数:To-B 28 ms,To-C / Coder 约 113 ms,Thinking 369 ms。

点开:间隔时间的 CCDF

请求间隔 CCDF

横轴是相邻请求的间隔。越靠右上,越「稀疏」。Thinking 明显更稀,To-B 挤在几十毫秒量级。

To-C 的爬升主要来自 text(约 4.4 QPS)。search 大约稳定在 1 QPS;image / file 更稀。To-B 里 api 约 21 QPS、text 约 3 QPS,两条曲线同涨同落,差在量不在节奏。

To-C 各 type 到达率

To-B api vs text 到达率


Token 形状

输入输出 token CCDF

左图输入、右图输出,都是 log-log CCDF。To-B 的输出掉得最快;Thinking 的输出一直拖到 10⁴–10⁵。Coder 输入在 10⁴ 附近有一块平台,很像「仓库/文件上下文被截到某个长度」。

token 分位数

若你关心 GPU 上到底是 prefill 忙还是 decode 忙,看到达 token/s 比看 QPS 更直接:

到达 token 速率

  • Coder:3.4 万 input token/s,四条里 prefill 最重。
  • Thinking:输出约 5.5k token/s,几乎赶上它自己的输入。
  • To-B:QPS 最高,输出 token/s 反而最低——短答把并发打满,把生成打不满。
点开:输入 vs 输出散点(每条随机 5000 点)

输入 vs 输出

To-B 贴在底部一条「短输出」带上。Thinking 往右上飞。两者几乎没有重叠,硬平均没有意义。

To-C 按 type 再拆,四类几乎是四种业务:

  • text:中位输入 790,像普通对话。
  • search:中位输入 5.3k,P99 1.7 万。检索拼进 prompt 之后,prefill 比 text 更重——尽管 QPS 只有 text 的四分之一,输入 token/s(6.8k)已经超过 text(5.6k)。
  • file:比 search 还长(中位 5.7k,P99 2.2 万),条数最少。
  • image:最短。输入中位 369,输出中位 69。更像「看图短答」。

To-C 各 type token 分位数

点开:To-C 各 type 的 token CCDF

To-C token CCDF

search / file 的输入长尾明显右移。image 输出掉得最快。

To-B 的 api 与 text 形状很像(输入中位 589 vs 479,输出中位 37 vs 46)。内部再按 type 分,分不出两种 serving 形态。

点开:To-B api vs text 的 token CCDF

To-B token CCDF

两条几乎叠在一起。不要指望用 type=text 把 B 端变成 C 端聊天。

各 type 到达 token/s

只 replay To-C 的 text,会低估 prefill:search + file 条数不到 23%,输入 token 却占 To-C 的大半。


会话与 KV 缓存

chat_id 在四条里都是 每条请求唯一。会话不是「同一个 chat_id 多行」,而是用 parent_chat_id 连成树。

flowchart LR
  r["根请求 parent = -1"] --> t2["第 2 轮 parent = 根的 chat_id"]
  t2 --> t3["第 3 轮"]

To-B 这棵树退化成 17.3 万个互不连接的点。

KV 块复用

  • 右图,多轮前缀: 子请求相对父请求,约 67–69% 的 16-token 块能对上。支持「同一会话粘到同一实例」。
  • 左图,全局复用: To-B 没有多轮,全局块却仍有 62% 见过。命中来自跨请求的系统提示 / 模板,不是聊天历史。

只为多轮做 session routing,会漏掉 B 端最大的那部分缓存。自动前缀缓存对 API 流量同样重要。Coder 全局复用最高(66%),Thinking 最低(46%)——长推理内容更「一次性」。

点开:会话树规模

| Trace | 会话树数量 | 多轮占比 | 会话大小 P99 | 最大轮次 |
| --- | ---: | ---: | ---: | ---: |
| To-C | 23,101 | 46.3% | 8 | 37 |
| To-B | 172,800 | 0% | 1 | 1 |
| Thinking | 9,612 | 11.1% | 5 | 16 |
| Coder | 26,406 | 38.6% | 9 | 54 |

中位数都是 1:大多数「会话」其实只有一轮。长尾才是 KV 增长的来源。

按 type 看,search / file 相对父请求的前缀命中 86–90%(检索页和文件上下文会在后续轮原样带着);但 search 在 type 内部的全局块复用只有 51%——每次检索内容更「新」。image 相反:多轮只有 8%,全局块复用却高达 85%,像是共享视觉模板。To-B 的 api / text 都是 0% 多轮、约 60% 全局复用,缓存来自模板而不是会话。

各 type 多轮与 KV 复用

前缀缓存:search / file 要靠会话粘性;image / To-B 更靠跨请求模板。Thinking、Coder 没有更细标签,拆不开。

系统 使用哪条 真正打满的是
高并发短 decode、API 网关 To-B 调度与 batch 槽位
前缀缓存、PD 分离、长 prefill Coder,其次 To-C KV 容量与 prefill
长 decode、弹性扩缩 Thinking max_tokens 与突发
带搜索/多模态的 C 端 chat To-C 中等 prefill + 多轮粘性

和 ShareGPT 默认压测的差别:To-B 更短更密;Coder / Thinking 输入长一个数量级;Thinking 输出长一到两个数量级;到达有爬升和尖峰,不是平稳泊松。

点开:和 BurstGPT、Mooncake、Azure 怎么搭配

这份数据 **没有** 天级节律,也 **没有** GPU SM。长周期突发请叠 [BurstGPT](https://github.com/HPMLL/BurstGPT)(213 天)。前缀哈希的另一种块大小(512)见 [Mooncake](https://github.com/kvcache-ai/Mooncake)。对话 vs 代码的一周到达见 [Azure LLM 2024](https://github.com/Azure/AzurePublicDataset/blob/master/AzureLLMInferenceDataset2024.md)。卡侧利用率见阿里 xMaaS / ASI GPU trace,和这份请求日志不是同一层。

块大小不要混:Bailian 是 16,Mooncake 是 512。


replay方法

克隆后 JSONL 走 Git LFS(合计约 300MB):

git lfs install
git clone https://github.com/alibaba-edu/qwen-bailian-usagetraces-anon.git
cd qwen-bailian-usagetraces-anon
git lfs pull

用官方 Trace Replayer(OpenAI 兼容 / TGI / AIBrix):

./client \
  --tokenizer tokenizer.json \
  --endpoint http://localhost:8080/v1/chat/completions \
  --api openai \
  --dataset bailian \
  --dataset-path qwen_traceA_blksz_16.jsonl \
  --scale-factor 1.0 \
  --model-name your-model

或 NVIDIA AIPerf:

aiperf profile \
  --model Qwen/Qwen3-0.6B \
  --endpoint-type chat \
  --streaming \
  --url localhost:8000 \
  --input-file qwen_traceA_blksz_16.jsonl \
  --custom-dataset-type bailian_trace \
  --fixed-schedule

有 timestamp 就用 open-loop / fixed-schedule,不要再套恒定并发。需要打更高 QPS 时用 scale-factor 压时间轴,尽量保留突发形状。

点开:字段坑(官方 FAQ)

多轮里,下一轮输入并不总是完整包含上一轮输出。缺的往往是 chat template 注入的 <think> / </think>,应用层组下一轮上下文时会剥掉。

最后一个块的哈希也可能对不上「输入+第一个输出 token」的朴素拼接:最后一块可能有 padding,第一个生成 token 会改写这块内容,哈希跟着变。这不是丢 token。详见仓库 docs/qa-context-growth-pattern.md。


思考

点开之前先想三秒。

To-B 平均正好 24 QPS,是不是均匀合成的?

不完全是。请求数整除时长,窗口可能经过抽取;但 10 秒峰到 37 QPS,间隔也不是常数。更像「截了两小时、平均 24」而不是恒定间隔生成器。

没有多轮,前缀缓存是不是就没用?

对 To-B 来说,会话前缀没用,**跨请求模板**还有。全局 16-token 块仍有约 62% 复用。

哪条 trace 最像「普通 ChatGPT 聊天」?

只有 To-C。Thinking / Coder 是专用形态;To-B 是 API。四条平均起来什么都不像。

这份数据能告诉我 GPU 利用率吗?

不能。没有 SM、HBM、功耗。只有请求到达和 KV 块。卡侧请另找集群 trace。

  • 推不出全天/全周节律(只有 2 小时)。
  • 推不出失败率、TTFT、排队。
  • 推不出四条在真实线上的流量占比——它们是并列切片,不是 mix 抽样。
  • 不能把「平均 token」当成百炼总负载。

参考文献与下载