Skip to content

[RFC] [AISBench] 支持Prefix Cache场景数据集生成及性能测评能力技术方案设计 #432

Description

@Libotry

1. 概述

1.1 简介

本方案面向AISBench建设Prefix Cache场景数据集生成及性能测评能力,覆盖Prefix Cache测试数据构造、真实业务流量模拟、理论命中率规划、推理服务指标采集及测评结果分析等完整流程。

用户可以根据业务请求长度特征、目标Prefix Cache命中率、Prefix组织方式、请求发送顺序及缓存部署形态,生成可控、可复现、可校验的Prefix Cache测试数据集,并通过AISBench执行性能测评,分析Prefix Cache对TTFT、吞吐、时延及KV Cache资源使用率的影响。

方案支持两类测试模式:

  1. 预热固定比例模式

    通过Warmup请求预先建立公共Prefix缓存,正式测试请求按照固定公共Prefix比例复用缓存,适合验证推理框架Prefix Cache基础能力及不同Prefix比例下的性能收益。

  2. 0warmup逐步填充模式

    不设置独立Warmup阶段,所有请求均纳入正式测评。每条请求相互独立,同一Prefix Group内的请求按照输入长度从短到长发送,前序请求逐步建立公共Prefix缓存水位,后续请求根据已建立的缓存水位产生理论命中,适合模拟真实业务中缓存从冷启动逐步增长的过程。

数据内容采用:

组级共享主Prefix+请求级唯一分叉Block+GSM8K多样本拼接Suffix+Block冲突检测。

其中:

  • 组级共享主Prefix用于构造预期缓存复用;
  • 唯一分叉Block用于保证请求在设计位置停止继续匹配;
  • GSM8K多样本拼接Suffix用于增强文本自然性;
  • Block冲突检测用于降低非公共区域偶然重复造成的额外缓存命中。

数据集生成完成后,AISBench输出数据Manifest和理论命中率校验报告,说明数据集是否达到目标理论命中率、偏差大小、最大可达命中率及偏差原因。

推理服务运行完成后,AISBench进一步采集服务端Prefix Cache指标,将目标命中率、数据集理论命中率和服务端实际命中率进行对比,区分数据构造偏差和服务端运行偏差。

1.2 动机

随着大模型上下文长度持续增长,以及RAG、长文档理解、代码生成和复杂Agent等应用的发展,Prefill阶段重复计算带来的时延和算力开销不断增加。

Prefix Cache通过复用请求之间相同Prefix对应的KV Cache,能够减少重复Prefill计算,降低TTFT并提升系统吞吐,是当前大模型推理系统的重要性能优化手段。

当前AISBench主要提供通用性能测评能力,在Prefix Cache专项测评方面存在以下不足:

  1. 主要支持固定输入长度,无法模拟真实业务中的变长请求及长尾分布;
  2. 缺少根据目标命中率自动规划Prefix长度的数据生成能力;
  3. 缺少统一的单Prefix、多Prefix和多租户流量建模;
  4. 缺少0warmup场景下缓存逐步建立过程的模拟能力;
  5. 静态公共Prefix比例无法准确表示考虑请求顺序后的理论命中率;
  6. 缺少多Prefix Group独立缓存水位和冷启动损失计算;
  7. 缺少自然文本Suffix及非预期Block重复检测;
  8. 缺少数据集生成结果的理论命中率校验;
  9. 缺少服务端Prefix Cache指标采集及多实例、DP域聚合;
  10. 缺少目标值、理论值和实际值之间的偏差分析。

vLLM相关Benchmark能够验证固定Prefix和多Prefix场景的基础性能,现有测试小工具能够基于GSM8K进行部分Prefix数据构造,但二者均缺少真实变长流量、顺序感知理论模型、可达性分析和理论与实际结果关联能力。

因此,需要在AISBench中建设完整的Prefix Cache场景数据集生成及性能测评能力,使AISBench不仅能够“运行Prefix Cache测试”,还能够解释:

  • 数据集为什么能够达到目标命中率;
  • 如果没有达到,理论偏差是多少;
  • 服务端实际命中率为什么与理论值存在差异;
  • 不同Prefix Group、实例和DP域分别表现如何。

1.3 目标

本方案目标包括:

  1. 支持固定长度、显式长度、截断正态分布、自定义区间分布及经验分布请求生成;
  2. 支持构造目标命中率为50%、60%、90%等不同场景的数据集;
  3. 支持单Prefix和多Prefix Group数据构造;
  4. 支持预热固定比例和0warmup逐步填充两类测试模式;
  5. 支持组内顺序、shuffle、interleave、weighted interleave等发送策略;
  6. 支持使用随机Token、GSM8K随机样本、指定样本及多样本拼接生成数据;
  7. 支持唯一分叉Block和minimum_non_shared_length约束;
  8. 支持组内Suffix冲突和跨组主Prefix冲突检测;
  9. 支持顺序感知理论命中率计算;
  10. 支持多Prefix Group独立缓存水位和冷启动损失计算;
  11. 支持目标理论命中率可达性分析;
  12. 支持Block对齐和理论命中率残差校准;
  13. 支持输出数据Manifest和理论命中率校验报告;
  14. 支持采集Prefix Cache Query、Hit、Hit Rate及KV Cache使用率;
  15. 支持实例级、DP域级和全局指标统计;
  16. 支持分析目标命中率、理论命中率和实际命中率之间的偏差;
  17. 保证相同配置、Tokenizer和随机种子下数据生成结果可复现;
  18. 普通数据集构造场景下,目标命中率与数据集理论命中率偏差默认控制在1个百分点以内;
  19. 单实例、固定Prefix等受控场景下,理论命中率与服务端实测命中率偏差建议控制在5个百分点以内;
  20. 多Prefix、多实例和真实业务流量场景下,允许更高运行偏差,但必须输出明确的偏差分析。

2. 用例分析

2.1 固定长度单Prefix场景

用户配置固定输入长度和固定公共Prefix比例。

例如:

request_count: 1000
input_length: 4096
target_prefix_ratio: 0.5
prefix_mode: single_prefix

每条请求包含:

2048 Token共享Prefix
+
唯一分叉Block
+
剩余自然Suffix

该场景适合:

  • 验证Prefix Cache是否生效;
  • 对齐vLLM固定Prefix Benchmark;
  • 对比0%、50%、60%、90%Prefix比例下的TTFT和吞吐;
  • 验证不同推理框架的基础缓存收益。

2.2 固定长度多Prefix场景

用户配置多个Prefix Group,每个组拥有独立的主Prefix。

例如:

Group A:500条请求
Group B:300条请求
Group C:200条请求

支持:

  • 分组连续发送;
  • 组间轮转;
  • 按权重交错;
  • 全局shuffle。

该场景用于模拟多个租户、多个系统Prompt或多个业务模板共享同一推理服务。

2.3 变长请求场景

用户根据真实业务请求长度特征配置变长请求。

支持以下方式:

  1. 固定长度;
  2. 显式长度列表;
  3. 截断正态分布;
  4. 自定义区间分布;
  5. 真实长度样本抽样。

截断正态分布示例:

input_length:
  mode: truncated_normal
  min: 1024
  max: 8192
  mean: 4096
  std: 1024

含义是:

  • 请求长度集中在4096 Token附近;
  • 长度波动受标准差控制;
  • 所有请求必须落在1024~8192 Token范围内;
  • 超出范围的样本重新采样或按配置处理。

区间分布示例:

input_length:
  mode: histogram
  bins:
    - range: [0, 1000]
      weight: 2
    - range: [1000, 2000]
      weight: 3
    - range: [2000, 4000]
      weight: 10
    - range: [4000, 8000]
      weight: 5

截断正态分布适合缺少真实业务数据时进行统计模拟;区间分布适合根据真实业务统计还原请求长度特征。

2.4 0warmup独立请求逐步填充场景

该场景中每条请求相互独立,不属于多轮对话,也不继承上一条请求的完整内容。

同一Prefix Group内的请求共享一条嵌套主Prefix,并按输入长度从短到长发送。

例如:

Request 1:1000 Token
Request 2:2000 Token
Request 3:3000 Token
Request 4:4000 Token

发送顺序:

Request 1→Request 2→Request 3→Request 4

每条请求结构:

MasterPrefix[0:planned_prefix_len]
+
UniqueDivergenceBlock
+
GSM8KSuffix

第一条请求发送前没有缓存,理论命中长度为0。

后续请求能够命中的长度,由该Prefix Group此前已建立的最长公共Prefix缓存水位决定。

该场景下,目标50%不能解释为每条请求都命中50%,而应解释为:

所有请求理论命中Token总数
÷
所有请求输入Token总数
=
50%

2.5 多Prefix 0warmup场景

多Prefix场景下,每个Prefix Group独立维护缓存水位。

例如:

Group A:A1→A2→A3
Group B:B1→B2→B3
Group C:C1→C2→C3

每个Group第一条请求均为冷启动请求,理论命中长度为0。

组间可以交错:

A1→B1→C1→A2→B2→C2

但必须保持每个组内部的顺序约束。

全局理论命中率必须按照Token总量加权计算,不能简单平均各组命中率。

2.6 真实文本数据场景

用户可以选择:

  • 随机Token;
  • 随机GSM8K样本;
  • 指定GSM8K样本;
  • 多条指定样本;
  • 多样本确定性拼接;
  • 用户自定义文本。

GSM8K数据既可以用于生成组级主Prefix,也可以用于生成自然Suffix。

当单条样本长度不足时,默认继续拼接其他样本,不建议无限重复同一条样本。

2.7 多实例和多DP场景

如果Prefix Cache为实例或DP本地缓存,则同一Prefix Group的请求进入不同实例或DP时,可能无法复用此前缓存。

支持两种缓存模型:

  1. 全局共享缓存:
watermark[group_id]
  1. DP本地缓存:
watermark[dp_id][group_id]

需要支持:

  • Prefix Group绑定实例或DP;
  • Sticky Routing;
  • 记录计划路由和实际路由;
  • 分别统计实例、DP及全局命中率。

2.8 理论值与实测值对比场景

数据集生成后输出:

  • 目标理论命中率;
  • 数据集理论命中率;
  • 数据构造偏差;
  • 最大可达理论命中率;
  • 校验状态。

服务端运行后输出:

  • 服务端实际命中率;
  • 理论与实际偏差;
  • 性能指标变化;
  • 多Prefix、实例和DP维度分析。

3. 方案设计

3.1 总体方案

总体架构分为六层。

3.1.1 场景配置层

负责接收用户配置,包括:

  • 请求数量;
  • 输入长度分布;
  • 目标命中率;
  • Prefix模式;
  • Prefix Group数量及权重;
  • Warmup模式;
  • 请求顺序;
  • 数据来源;
  • Block Size;
  • 最小非共享长度;
  • 冲突检测策略;
  • 指标采集地址。

3.1.2 Workload规划层

负责:

  • 生成请求长度序列;
  • 分配Prefix Group;
  • 规划请求顺序;
  • 计算目标命中Token预算;
  • 规划Prefix长度;
  • 执行可达性检查;
  • 执行Block残差校准。

3.1.3 数据内容生成层

负责:

  • 生成组级主Prefix;
  • 生成请求级唯一分叉Block;
  • 拼接GSM8K自然Suffix;
  • 执行Token级截断和补齐;
  • 执行Block冲突检测;
  • 输出最终Prompt。

3.1.4 理论仿真与校验层

负责:

  • 模拟单Prefix或多Prefix缓存水位;
  • 计算请求级理论命中长度;
  • 计算组级和全局理论命中率;
  • 判断目标是否可达;
  • 计算理论偏差;
  • 输出校验报告。

3.1.5 测评执行与指标采集层

负责:

  • 按最终顺序发送请求;
  • 执行Cold、Warmup、Hot、Compare或Progressive Prefix Fill;
  • 采集性能指标;
  • 采集Prefix Cache服务端指标;
  • 采集实例和DP维度指标。

3.1.6 结果分析层

负责:

  • 关联数据Manifest和运行结果;
  • 对比目标值、理论值和实际值;
  • 计算数据构造偏差和运行实现偏差;
  • 输出专项测评报告。

总体流程:

用户配置
  ↓
请求长度生成
  ↓
Prefix Group分配
  ↓
请求顺序规划
  ↓
目标可达性检查
  ↓
Prefix长度及缓存水位规划
  ↓
主Prefix/分叉Block/Suffix生成
  ↓
Block冲突检测
  ↓
数据Manifest和理论报告
  ↓
AISBench执行测评
  ↓
服务端Cache指标采集
  ↓
理论与实际偏差分析
  ↓
专项测评报告

3.2 技术选型

3.2.1 Token级数据生成

所有长度计算和数据截断均基于模型Tokenizer完成,不使用字符数估算。

原因包括:

  • 不同Tokenizer下字符和Token映射不同;
  • Prefix Cache按照Token及Block处理;
  • 理论命中率需要与服务端Token统计口径一致。

生成流程:

原始文本
→Tokenizer Encode
→Token ID拼接
→Token级截断/补齐
→生成Prompt
→必要时重新Encode校验

3.2.2 长度分布模型

支持:

模式 用途
fixed 固定长度基准测试
explicit 用户指定每条请求长度
truncated_normal 模拟均值集中、两侧减少的真实业务分布
histogram 根据区间权重还原业务统计
empirical 从真实业务长度样本抽样

截断正态分布优先采用拒绝采样,避免简单裁剪造成最小值和最大值处样本集中。

3.2.3 缓存粒度模型

数据规划需要基于服务端KV Cache Block Size。

优先级:

  1. 从服务端配置自动获取;
  2. 从服务启动参数获取;
  3. 用户显式配置;
  4. 使用框架默认值并输出告警。

所有规划Prefix长度原则上需要按照Block Size向下对齐:

aligned_prefix_len
=
floor(raw_prefix_len/block_size)×block_size

3.2.4 随机性与可复现

使用局部随机生成器。

随机种子由以下信息组合:

global_seed
+dataset_id
+target_ratio
+prefix_group_id
+request_id
+retry_count

相同配置、Tokenizer、数据源版本和随机种子应生成相同数据。

3.2.5 数据存储格式

建议输出:

dataset/
├──data.jsonl
├──manifest.json
├──request_metadata.jsonl
└──validation_report.json

其中:

  • data.jsonl:实际发送给模型的数据;
  • manifest.json:数据集配置及整体元数据;
  • request_metadata.jsonl:请求级Prefix、水位和理论命中信息;
  • validation_report.json:目标可达性和理论命中率校验结果。

3.2.6 指标采集方式

通过推理服务提供的/metrics或等价接口采集Prefix Cache指标。

对于累计Counter指标,采用运行前后快照差值:

metric_delta
=
metric_after-metric_before

多实例和多DP全局结果采用Counter增量加权,不直接平均命中率。

3.3 功能与性能设计

3.3.1 请求长度生成

支持固定、显式、截断正态、区间及经验分布。

生成后输出:

  • 请求数量;
  • 最小长度;
  • 最大长度;
  • 平均长度;
  • P50、P90、P95、P99;
  • 各长度区间请求数;
  • 随机种子。

所有请求长度需要满足模型最大上下文约束:

input_len+output_len≤max_model_len

3.3.2 Prefix Group分配

支持:

  1. 单Prefix;
  2. 多Prefix平均分配;
  3. 按请求数量分配;
  4. 按请求权重分配;
  5. 用户显式指定请求所属Group。

每条请求必须具有唯一的prefix_group_id

多Prefix Group之间的主Prefix相互独立,同一Group内请求从同一条主Prefix头部截取。

3.3.3 预热固定比例模式

对每条请求:

raw_prefix_len_i
=
input_len_i×target_prefix_ratio

执行Block对齐:

prefix_len_i
=
align_down(raw_prefix_len_i,block_size)

公共Prefix预先通过Warmup请求写入缓存。

正式请求的理论命中长度为规划公共Prefix长度。

该模式主要用于:

  • 对齐vLLM固定Prefix测试;
  • 验证框架Cache能力;
  • 分析不同Prefix比例性能收益。

3.3.4 0warmup顺序感知模式

对第i条请求定义:

  • L_i:输入Token长度;
  • P_i:规划共享Prefix长度;
  • C_i:发送前所属Group缓存水位;
  • H_i:理论命中Token长度。

计算:

H_i=min(P_i,C_i)

请求完成后:

C_(i+1)=max(C_i,P_i)

若同组请求Prefix长度单调递增:

P_1≤P_2≤...≤P_n

则:

H_1=0
H_2=P_1
H_3=P_2
...
H_n=P_(n-1)

全局理论命中率为:

order_aware_hit_ratio
=
sum(H_i)/sum(L_i)

用户配置的50%、60%、90%约束该指标,而不是单条请求的静态Prefix比例。

3.3.5 目标命中率反向规划

用户配置:

target_hit_ratio=T

目标命中Token总量:

target_hit_tokens
=
T×sum(L_i)

系统需要规划每条请求的P_i,使:

sum(H_i)≈target_hit_tokens

规划过程:

  1. 根据请求长度和目标比例初始化基础规划比例;
  2. 对Prefix长度执行Block对齐;
  3. 按最终请求顺序仿真缓存水位;
  4. 计算当前理论命中率;
  5. 使用二分搜索调整基础规划比例;
  6. 使用Block级残差校准微调部分请求;
  7. 达到允许偏差或迭代上限。

3.3.6 目标可达性分析

每条请求需要保留最小非共享区域:

max_prefix_i
=
align_down(
  input_len_i-minimum_non_shared_length,
  block_size
)

将所有请求Prefix设置为最大允许值,并按最终请求顺序进行缓存水位仿真,得到最大可达理论命中率:

max_reachable_hit_ratio

若:

target_hit_ratio>max_reachable_hit_ratio

则目标不可达。

系统需要输出:

  • 目标命中率;
  • 最大可达命中率;
  • 不可达原因;
  • 调整建议。

可调整方式包括:

  • 增加请求数量;
  • 减少Prefix Group数量;
  • 增加每组请求数;
  • 缩小最大请求与其他请求之间的长度差;
  • 减少最小非共享区域;
  • 改为预热模式。

3.3.7 多Prefix独立缓存水位

每个Prefix Group独立维护:

watermark[group_id]

初始化:

watermark[group_id]=0

请求理论命中:

hit_len_i
=
min(
  planned_prefix_len_i,
  watermark[group_id]
)

请求完成后:

watermark[group_id]
=
max(
  watermark[group_id],
  planned_prefix_len_i
)

不同Group水位互不影响。

每个Group第一条请求理论命中长度均为0。

如果每个Group只有一条请求,则全局理论命中率为0。

3.3.8 多Prefix全局目标分配

支持两种目标范围。

全局统一目标

只要求所有Group按Token加权后的全局理论命中率达到目标。

target:
  scope: global
  hit_ratio: 0.50

各组可以拥有不同理论命中率。

组级独立目标

为每个Group分别配置目标。

target:
  scope: per_group
  groups:
    - id: group_a
      hit_ratio: 0.50
    - id: group_b
      hit_ratio: 0.60

全局目标模式下,初始组级命中Token预算按输入Token权重分配:

target_hit_tokens_g
=
target_global_hit_tokens
×
group_input_tokens
/
global_input_tokens

然后根据各组冷启动损失和最大可达能力重新调整预算。

3.3.9 请求发送策略

支持:

策略 含义
sequential 按数据生成顺序发送
shuffle 全局随机打乱
grouped 同组请求连续发送
interleave 多组轮转交错
weighted_interleave 按Group流量权重交错
input_len_asc 按输入长度升序
input_len_desc 按输入长度降序
prefix_len_asc 按Prefix长度升序
prefix_len_desc 按Prefix长度降序

0warmup模式下,组内顺序属于理论命中率的一部分。

数据生成完成后,如果重新排序或shuffle,原理论校验结果失效,必须重新执行仿真和校验。

3.3.10 组间发送与理论命中率

在以下理想条件下:

  • 每个Group缓存独立;
  • 缓存容量充足;
  • 不发生淘汰;
  • Group内顺序保持不变;
  • 请求进入相同可见缓存域;

组间如何交错理论上不会改变总命中Token数。

但实际运行中,组间交错可能引入:

  • Cache容量竞争;
  • LRU淘汰;
  • 多实例路由;
  • 多DP路由;
  • 请求处理时序变化。

因此,组间发送策略必须记录在Manifest中,并在运行报告中分析其对实际命中率的影响。

3.3.11 唯一分叉Block

每条请求在公共Prefix后插入请求级唯一分叉Block。

结构:

SharedPrefix
+
DivergenceBlock
+
NaturalSuffix

唯一分叉Block用于保证请求在规划位置立即发生差异。

生成种子包含:

prefix_group_id
+request_id
+global_seed

需要排除:

  • BOS;
  • EOS;
  • PAD;
  • Chat Template控制Token;
  • 其他特殊Token。

同一Prefix Group内首个非公共Block必须唯一。

3.3.12 minimum_non_shared_length

minimum_non_shared_length表示:

每条请求在公共Prefix后必须保留的最小非共享Token长度。

主要作用:

  1. 保证公共Prefix后存在足够的差异区域;
  2. 限制单条请求最大可规划Prefix长度;
  3. 为唯一分叉Block和自然Suffix预留空间;
  4. 降低Suffix继续匹配造成额外缓存命中的风险。

约束:

planned_prefix_len_i
≤
input_len_i-minimum_non_shared_length

推荐默认:

minimum_non_shared_length=block_size

例如Block Size为16,则每条请求至少保留16 Token非共享区域。

需要说明:对于采用父链Hash的标准Prefix Cache,真正决定命中停止的是第一个不同Block及其父链上下文。因此,minimum_non_shared_length的主要价值是保证存在完整的非共享Block,而不是要求整个Suffix全部唯一。

3.3.13 GSM8K多样本拼接Suffix

自然Suffix生成流程:

选择GSM8K样本
→Tokenizer Encode
→插入样本分隔符
→继续选择其他样本
→达到目标长度
→Token级截断

支持:

  • 确定性随机选择;
  • 指定样本;
  • 指定样本后拼接其他样本;
  • 多指定样本循环;
  • 自定义分隔符。

默认不建议重复同一条GSM8K样本补齐长Suffix,避免形成周期性重复。

3.3.14 Block冲突检测

Block冲突检测分为两类。

组内非共享区域检测

检查:

  • 首个非共享Block是否唯一;
  • 连续Block窗口是否重复;
  • Suffix是否高度重复。
跨组主Prefix检测

检查不同Prefix Group主Prefix是否存在非预期长公共Prefix。

对于采用父链Hash的Prefix Cache:

  • 首个非共享Block及父链不同后,后续相同原始Block通常不会形成Prefix Cache命中;
  • 因此首个非共享Block唯一是强制约束;
  • 后续Block窗口重复可作为风险诊断,不一定需要强制拒绝。

建议支持缓存匹配语义配置:

cache_match_semantics: chained_prefix

可选:

  • chained_prefix
  • independent_block
  • content_defined_chunk

不同模式采用不同冲突检测强度。

3.3.15 冲突处理策略

按以下顺序处理:

  1. 重新生成唯一分叉Block;
  2. 更换GSM8K样本序列;
  3. 在样本边界加入自然请求标识;
  4. 达到最大重试次数后按配置处理。

支持:

  • error
  • warn
  • accept_with_warning

冲突检测结果写入数据Manifest和请求级元数据。

3.3.16 数据Manifest

数据Manifest是数据集的元数据说明书,不是实际Prompt数据。

Manifest记录:

  • 数据集ID;
  • 生成器版本;
  • Tokenizer;
  • Block Size;
  • 随机种子;
  • 请求数量;
  • 长度分布;
  • Prefix模式;
  • Prefix Group;
  • 目标命中率;
  • 请求发送顺序;
  • Suffix数据来源;
  • 最小非共享长度;
  • 理论命中率;
  • 最大可达命中率;
  • 校验状态。

请求级元数据记录:

  • request_id
  • prefix_group_id
  • order_index
  • input_len
  • planned_prefix_len
  • cached_watermark_before
  • expected_hit_len
  • expected_hit_ratio
  • cached_watermark_after
  • divergence_len
  • suffix_len
  • 冲突检测状态。

Manifest用于:

  • 数据复现;
  • 理论命中率校验;
  • 执行结果关联;
  • 多Prefix分析;
  • 问题定位。

3.3.17 理论命中率校验报告

数据集生成完成后必须输出理论校验报告。

核心指标:

target_hit_ratio
designed_hit_ratio
=
sum(expected_hit_tokens)
/
sum(input_tokens)
signed_deviation
=
designed_hit_ratio-target_hit_ratio
absolute_deviation
=
abs(signed_deviation)

建议优先使用“百分点”表达偏差。

例如:

目标理论命中率:50.00%
数据集理论命中率:49.80%
有符号偏差:-0.20个百分点
绝对偏差:0.20个百分点

校验状态:

状态 默认条件
PASS 偏差≤1个百分点
PASS_WITH_WARNING 1~2个百分点
FAIL 偏差>2个百分点或目标不可达

对于请求数量较多、长度较长的数据集,建议将构造偏差控制在0.5个百分点以内。

3.3.18 服务端指标采集

采集指标包括:

  • Prefix Cache Query;
  • Prefix Cache Hit;
  • Prefix Cache Hit Rate;
  • KV Cache Usage;
  • Cache Eviction;
  • 本地Cache命中;
  • 外置KV Cache命中;
  • 实例级Cache指标;
  • DP域级Cache指标。

累计Counter采用前后差值。

例如:

queries_delta
=
queries_after-queries_before
hits_delta
=
hits_after-hits_before
actual_hit_rate
=
hits_delta/queries_delta

3.3.19 多实例和DP聚合

每个实例或DP独立计算:

dp_hit_rate
=
dp_hits_delta/dp_queries_delta

全局结果:

global_hit_rate
=
sum(dp_hits_delta)
/
sum(dp_queries_delta)

禁止直接对各DP命中率做算术平均。

若缓存为DP本地缓存,理论仿真需要维护:

watermark[dp_id][group_id]

若缓存为全局共享缓存,则维护:

watermark[group_id]

3.3.20 性能指标

Prefix Cache性能测评需要至少输出:

  • TTFT平均值;
  • TTFT P50/P90/P95/P99;
  • TPOT;
  • E2E Latency;
  • Request Throughput;
  • Output Token Throughput;
  • QPS;
  • 请求成功率;
  • 超时率;
  • KV Cache使用率;
  • Prefix Cache命中率。

支持对比:

  • Cold vs Hot;
  • 不同目标命中率;
  • 单Prefix vs多Prefix;
  • 不同发送顺序;
  • 0warmup vs Warmup;
  • 不同推理框架;
  • 不同部署配置。

3.3.21 理论值与实测值偏差

需要区分两类偏差。

数据构造偏差
construction_deviation
=
designed_hit_ratio-target_hit_ratio

用于评价数据集生成准确性。

建议普通场景控制在1个百分点以内。

运行实现偏差
runtime_deviation
=
actual_hit_rate-designed_hit_ratio

用于评价服务端运行与理论模型的一致性。

参考范围:

场景 理论值与实测值建议偏差
固定长度、单Prefix、单实例 ≤5个百分点
固定长度、多Prefix 2~5个百分点
变长、短到长、0warmup 3~5个百分点
多Prefix、多实例或多DP 5~10个百分点
真实业务流量及容量压力 允许超过10个百分点,但需明确归因

以上范围为工程建议,不应直接作为所有推理框架的统一强约束。最终验收阈值需结合服务端指标语义和实际测试结果确定。

3.3.22 偏差归因

理论值与实际值不一致时,分析以下因素:

  1. Block Size配置不一致;
  2. 指标统计单位不同;
  3. 请求实际处理顺序与提交顺序不同;
  4. Prefix Group请求进入不同实例或DP;
  5. Cache容量不足或发生淘汰;
  6. 服务端存在历史缓存残留;
  7. Suffix发生非预期重复;
  8. 外置KV Cache与本地Cache统计重叠;
  9. Chat Template或特殊Token导致实际Token序列变化;
  10. 服务端只缓存完整Block;
  11. Counter采集区间不准确;
  12. 请求失败或超时未计入预期统计。

3.3.23 性能要求

数据生成性能建议满足:

  1. 支持至少10万条请求的流式生成;
  2. 大规模数据生成不要求将所有Prompt同时保存在内存;
  3. Block Hash采用增量注册;
  4. 支持分组并行Tokenize;
  5. 同一随机种子下生成结果稳定;
  6. 数据生成失败时能够保留错误上下文和部分Manifest;
  7. 理论仿真时间应明显低于实际测评时间;
  8. 指标采集不得显著影响请求发送性能。

3.4 安全隐私与DFX设计

3.4.1 数据安全

默认使用公开数据集,不包含用户私有业务数据。

用户导入自定义数据时支持:

  • 原始文本不打印到普通日志;
  • Manifest默认只记录样本ID和Hash;
  • 可配置是否保存完整Prompt;
  • 文件路径脱敏;
  • 认证信息和服务地址脱敏;
  • 报告中不展示敏感Header。

3.4.2 可复现性

保存以下信息:

  • AISBench版本;
  • 数据生成器版本;
  • 配置文件;
  • Tokenizer名称和版本;
  • 数据集版本;
  • Block Size;
  • Random Seed;
  • Prefix Group配置;
  • 请求顺序;
  • 路由策略;
  • 冲突重试次数;
  • 理论仿真结果。

3.4.3 可诊断性

日志分为:

  • 配置校验日志;
  • 长度生成日志;
  • Prefix规划日志;
  • 冲突检测日志;
  • 理论仿真日志;
  • 指标采集日志;
  • 报告生成日志。

错误需要包含:

  • 错误阶段;
  • 数据集ID;
  • Request ID;
  • Prefix Group;
  • 当前重试次数;
  • 相关配置字段;
  • 建议处理方式。

3.4.4 容错设计

需要处理:

  • 长度范围非法;
  • 平均值不在截断区间内;
  • 请求数量为0;
  • 目标命中率不在0~1;
  • 目标命中率不可达;
  • Prefix长度超过请求长度;
  • 非共享长度不足;
  • Tokenizer加载失败;
  • GSM8K数据不足;
  • Block冲突重试超限;
  • Metrics地址不可访问;
  • Counter重置;
  • 服务实例重启;
  • 部分请求失败;
  • 多实例指标缺失。

3.4.5 兼容性

方案需要考虑:

  • vLLM;
  • vLLM-Ascend;
  • MindIE;
  • SGLang;
  • OpenAI兼容推理服务;
  • 外置KV Cache系统。

不同框架使用统一逻辑指标模型,通过Adapter映射框架原始指标。

3.4.6 可扩展性

后续可扩展:

  • RAG Prefix Cache;
  • 多级KV Cache;
  • KV Cache Offload;
  • PD分离Cache;
  • Cache淘汰专项测试;
  • Cache容量压力测试;
  • Agent历史轨迹Cache;
  • Code Repository Prefix;
  • 用户真实流量回放。

3.5 编程与调用设计

3.5.1 编程模型基本设计

核心对象如下。

PrefixCacheDatasetConfig

描述完整数据生成配置。

字段包括:

  • dataset_id
  • seed
  • request_count
  • block_size
  • input_length
  • target
  • prefix
  • suffix
  • request_order
  • execution
  • routing
  • validation
RequestPlan

描述单条请求规划结果。

字段包括:

  • request_id
  • prefix_group_id
  • input_len
  • planned_prefix_len
  • divergence_len
  • suffix_len
  • order_index
  • planned_dp_id
PrefixGroupPlan

描述单个Prefix Group。

字段包括:

  • group_id
  • request_count
  • master_prefix_len
  • target_hit_tokens
  • max_reachable_hit_ratio
  • cold_start_loss
DatasetManifest

描述数据集整体元数据。

ValidationReport

描述目标可达性及理论校验结果。

RuntimeMetrics

描述服务端实际性能和Cache指标。

整体编程流程:

config = PrefixCacheDatasetConfig.load(config_path)

lengths = LengthGenerator.generate(
    config.input_length,
    config.request_count,
    config.seed,
)

requests = RequestPlanner.create(lengths)

groups = PrefixGroupAllocator.allocate(
    requests,
    config.prefix,
)

RequestOrderPlanner.sort_within_groups(
    groups,
    config.request_order,
)

reachability = ReachabilityValidator.validate(
    groups,
    config,
)

PrefixPlanner.plan(
    groups,
    config.target,
    config.block_size,
    config.prefix.minimum_non_shared_length,
)

MasterPrefixGenerator.generate(groups, config)

RequestContentGenerator.generate(
    groups,
    config.suffix,
)

CollisionValidator.validate_and_repair(
    groups,
    config.suffix.collision_detection,
)

ordered_requests = RequestOrderPlanner.merge_groups(
    groups,
    config.request_order,
)

simulation = WatermarkSimulator.simulate(
    ordered_requests,
    config.routing.cache_scope,
)

validation = TheoreticalHitRateValidator.validate(
    simulation,
    config.target,
    config.validation,
)

DatasetWriter.write(
    ordered_requests,
    groups,
    simulation,
    validation,
)

runtime_result = PrefixCacheBenchmarkRunner.run(
    ordered_requests,
    config.execution,
)

report = PrefixCacheReportBuilder.build(
    manifest=DatasetManifest.load(),
    validation=validation,
    runtime_result=runtime_result,
)

3.5.2 接口定义与设计

数据集生成接口
def generate_prefix_cache_dataset(
    config: PrefixCacheDatasetConfig,
) -> DatasetGenerationResult:
    ...

返回:

  • 数据文件路径;
  • Manifest路径;
  • 请求级元数据路径;
  • 理论校验报告路径。
长度生成接口
def generate_request_lengths(
    config: LengthDistributionConfig,
    request_count: int,
    seed: int,
) -> list[int]:
    ...
Prefix Group分配接口
def allocate_prefix_groups(
    requests: list[RequestPlan],
    config: PrefixGroupConfig,
) -> dict[str, PrefixGroupPlan]:
    ...
目标可达性接口
def validate_target_reachability(
    groups: dict[str, PrefixGroupPlan],
    target: TargetHitRateConfig,
    block_size: int,
    minimum_non_shared_length: int,
) -> ReachabilityResult:
    ...
Prefix规划接口
def plan_prefix_lengths(
    groups: dict[str, PrefixGroupPlan],
    target: TargetHitRateConfig,
    block_size: int,
    minimum_non_shared_length: int,
) -> PrefixPlanningResult:
    ...
主Prefix生成接口
def generate_master_prefixes(
    groups: dict[str, PrefixGroupPlan],
    data_source: DataSourceConfig,
    tokenizer: TokenizerProtocol,
) -> dict[str, list[int]]:
    ...
Suffix生成接口
def generate_request_suffix(
    request: RequestPlan,
    config: SuffixGenerationConfig,
    tokenizer: TokenizerProtocol,
) -> SuffixGenerationResult:
    ...
冲突检测接口
def validate_block_collisions(
    request_tokens: list[int],
    group_registry: BlockRegistry,
    config: CollisionDetectionConfig,
) -> CollisionValidationResult:
    ...
缓存水位仿真接口
def simulate_prefix_cache_watermarks(
    requests: list[RequestPlan],
    cache_scope: str,
) -> PrefixCacheSimulationResult:
    ...
理论命中率校验接口
def validate_theoretical_hit_rate(
    simulation: PrefixCacheSimulationResult,
    target_hit_ratio: float,
    pass_tolerance: float,
    warning_tolerance: float,
) -> ValidationReport:
    ...
指标采集接口
def collect_prefix_cache_metrics(
    endpoints: list[str],
    before_snapshot: MetricsSnapshot,
    after_snapshot: MetricsSnapshot,
) -> PrefixCacheRuntimeMetrics:
    ...
报告生成接口
def build_prefix_cache_report(
    manifest: DatasetManifest,
    validation: ValidationReport,
    runtime_metrics: PrefixCacheRuntimeMetrics,
    performance_result: PerformanceResult,
) -> PrefixCacheReport:
    ...

3.5.3 编程手册设计

配置示例:固定长度单Prefix
prefix_cache_dataset:
  dataset_id: fixed_single_prefix_50
  seed: 42
  request_count: 1000
  block_size: 16

  input_length:
    mode: fixed
    value: 4096

  target:
    scope: global
    hit_ratio: 0.50
    semantics: static_prefix_ratio

  prefix:
    mode: single_prefix
    minimum_non_shared_length: 16

  suffix:
    source: gsm8k
    mode: concat
    divergence_block: true

  request_order:
    mode: sequential

  execution:
    mode: compare
    warmup_rounds: 1

  validation:
    pass_tolerance: 0.01
    warning_tolerance: 0.02
配置示例:0warmup变长单Prefix
prefix_cache_dataset:
  dataset_id: progressive_single_prefix_50
  seed: 42
  request_count: 1000
  block_size: 16

  input_length:
    mode: truncated_normal
    min: 1024
    max: 8192
    mean: 4096
    std: 1024

  target:
    scope: global
    hit_ratio: 0.50
    semantics: order_aware_global

  prefix:
    mode: single_prefix
    nested_prefix: true
    minimum_non_shared_length: 16

  suffix:
    source: gsm8k
    mode: concat
    divergence_block: true

    collision_detection:
      enabled: true
      cache_match_semantics: chained_prefix
      first_non_shared_block_unique: true
      window_blocks: 2

  request_order:
    within_group: input_len_asc

  execution:
    mode: progressive_prefix_fill
    warmup_rounds: 0

  planning:
    reachability_check: true
    base_ratio_search: binary_search
    block_residual_calibration: true
    max_iterations: 1000

  validation:
    pass_tolerance: 0.01
    warning_tolerance: 0.02
    on_failure: error
配置示例:0warmup多Prefix
prefix_cache_dataset:
  dataset_id: progressive_multi_prefix_50
  seed: 42
  request_count: 1000
  block_size: 16

  input_length:
    mode: histogram
    bins:
      - range: [1024, 2048]
        weight: 3
      - range: [2048, 4096]
        weight: 5
      - range: [4096, 8192]
        weight: 2

  target:
    scope: global
    hit_ratio: 0.50
    semantics: order_aware_global

  prefix:
    mode: multi_prefix
    nested_prefix: true
    minimum_non_shared_length: 16

    groups:
      - id: customer_a
        request_weight: 0.50
      - id: customer_b
        request_weight: 0.30
      - id: customer_c
        request_weight: 0.20

  request_order:
    within_group: input_len_asc
    across_group: weighted_interleave
    preserve_group_dependency: true

  execution:
    mode: progressive_prefix_fill
    warmup_rounds: 0

  routing:
    cache_scope: dp_local
    sticky_routing: true
    bind_prefix_group_to_dp: true

  validation:
    pass_tolerance: 0.01
    warning_tolerance: 0.02
命令行示例
ais_bench prefix-cache generate \
  --config prefix_cache.yaml \
  --output ./prefix_cache_dataset
ais_bench prefix-cache validate \
  --dataset ./prefix_cache_dataset
ais_bench prefix-cache run \
  --dataset ./prefix_cache_dataset \
  --url http://127.0.0.1:8000/v1/completions \
  --metrics-url http://127.0.0.1:8000/metrics
ais_bench prefix-cache report \
  --run-dir ./outputs/run_001
输出目录示例
outputs/run_001/
├──dataset/
│  ├──data.jsonl
│  ├──manifest.json
│  ├──request_metadata.jsonl
│  └──validation_report.json
├──runtime/
│  ├──request_results.jsonl
│  ├──performance_metrics.json
│  └──prefix_cache_metrics.json
└──report/
   ├──prefix_cache_report.json
   └──prefix_cache_report.html

4. 缺点和风险

4.1 理论模型依赖服务端缓存语义

不同推理框架可能采用父链Hash、独立Block Hash或内容定义分块。

如果AISBench理论模型与服务端实际缓存匹配语义不一致,理论命中率可能产生系统性偏差。

4.2 Block Size获取不准确

若配置的Block Size与服务端不一致,会影响:

  • Prefix对齐;
  • 最大可达命中率;
  • 唯一分叉Block;
  • 理论命中率;
  • 冲突检测。

4.3 提交顺序与实际处理顺序不一致

并发场景下,服务端可能改变Prefill执行顺序,导致实际缓存水位建立过程与理论提交顺序不一致。

4.4 多实例和多DP路由影响

同一Prefix Group请求被路由到不同实例或DP后,本地缓存可能无法复用。

若没有Sticky Routing,理论值可能明显高于实际值。

4.5 Cache容量和淘汰影响

理论模型默认缓存容量足够且不会淘汰。

多Prefix Group或长上下文场景中可能发生LRU淘汰,使实际命中率下降。

4.6 历史缓存污染

测试前服务端可能已有相同Prefix缓存,导致第一条请求产生非预期命中。

Cold场景需要支持服务重启、缓存清理或使用唯一Salt隔离。

4.7 自然文本无法完全代表真实业务

GSM8K多样本拼接能够提升文本自然性,但与真实RAG文档、代码上下文或业务Prompt仍存在差异。

4.8 高目标命中率可能不可达

以下情况可能导致90%等高命中目标不可达:

  • 请求数量过少;
  • Prefix Group数量过多;
  • 每组请求过少;
  • 最大请求占总Token比例过高;
  • 最小非共享长度过大。

4.9 理论值与服务端指标口径不同

理论命中率可能按Token计算,服务端指标可能按Block、Query或Cache Entry统计,二者不能直接比较。

需要在Adapter中明确指标单位。

4.10 Block冲突检测存在额外开销

大规模长上下文数据集进行全量Block窗口Hash检测,会增加生成时间和内存消耗。

4.11 组级预算分配不唯一

相同全局目标可以通过多种组级命中率组合实现。

需要保存组级预算分配策略,保证结果可解释和可复现。

4.12 理论与实测误差无法完全消除

即使数据构造完全正确,实际运行仍会受到调度、路由、淘汰、Counter采样和请求失败等因素影响。

因此,运行一致性应采用合理误差阈值和偏差分析,而不是要求理论值与实测值完全相等。

5. 现有技术

5.1 vLLM Prefix Cache Benchmark

vLLM相关Benchmark能够支持:

  • 固定Prefix长度;
  • 固定输入长度;
  • 随机Token生成;
  • 多Prefix数量配置;
  • 请求shuffle;
  • 基础性能测评。

主要不足:

  • 缺少真实变长分布;
  • 缺少目标命中率自动构造;
  • 缺少顺序感知理论模型;
  • 缺少多Prefix独立缓存水位;
  • 缺少理论命中率校验;
  • 缺少理论值与实际值偏差分析。

5.2 现有测试小工具

测试小工具能够支持:

  • GSM8K数据源;
  • 基于数据集生成Prefix;
  • prefix_num多Prefix;
  • repeat_rate公共Prefix比例;
  • 随机Token分叉;
  • 请求shuffle;
  • 多Pod指标采集。

主要不足:

  • 固定随机分叉Token不足以保证完整Block分叉;
  • 后缀可能重复单条GSM8K;
  • 缺少Block冲突检测;
  • 缺少变长区间分布;
  • 缺少0warmup顺序感知命中率;
  • 缺少目标可达性分析;
  • 缺少数据Manifest和理论校验报告;
  • 缺少多Prefix Group业务语义和组级分析。

5.3 AISBench现有性能评测能力

AISBench已有能力包括:

  • OpenAI兼容接口请求发送;
  • 请求并发控制;
  • TTFT、TPOT、吞吐和时延统计;
  • 数据集读取;
  • 结果保存;
  • 多类推理服务适配。

本方案主要复用AISBench已有执行和性能统计底座,新增Prefix Cache专项Workload生成、理论模型、指标采集和报告分析能力。

5.4 本方案新增能力

相较vLLM Benchmark和测试小工具,本方案新增核心能力包括:

  1. 变长请求长度分布生成;
  2. 目标命中率自动反向规划;
  3. 0warmup顺序感知理论命中率;
  4. 多Prefix Group独立缓存水位;
  5. 组级和全局可达性分析;
  6. 唯一分叉Block和最小非共享长度;
  7. GSM8K多样本自然Suffix;
  8. Block冲突检测;
  9. 数据Manifest;
  10. 理论命中率校验报告;
  11. 多实例、DP域指标聚合;
  12. 目标、理论和实测三层偏差分析。

6. 未解决问题

6.1 Block Size自动发现

不同框架暴露Block Size的方式不同,需要进一步设计统一发现机制。

6.2 服务端缓存匹配语义识别

需要明确vLLM、vLLM-Ascend、MindIE、SGLang和外置KV Cache的缓存Key及父链关系。

6.3 指标统计单位统一

需要建立统一指标语义:

  • Token Hit;
  • Block Hit;
  • Query Hit;
  • Cache Entry Hit。

6.4 并发场景理论模型

需要研究并发请求在Prefill开始、Block写入和Prefill完成不同时间点对缓存可见性的影响。

6.5 服务端实际处理顺序采集

需要评估是否能获取:

  • Prefill开始时间;
  • Prefill结束时间;
  • Cache Block写入时间;
  • 实际路由实例;
  • 实际DP域。

6.6 Cache容量和淘汰仿真

当前理论模型主要计算缓存复用,不完整模拟Cache容量和LRU淘汰。

6.7 多级缓存模型

需要进一步支持:

  • 本地GPU/NPU KV Cache;
  • Host Cache;
  • 外置KV Cache;
  • 多级命中优先级。

6.8 真实业务数据接入

后续需要支持真实业务日志脱敏导入,并根据真实Prefix相似度构造测试数据。

6.9 高目标命中率最优规划算法

当前采用二分搜索和Block残差校准,复杂多Prefix场景可能需要整数规划或约束优化。

6.10 不同框架验收阈值

理论值与实测值偏差受框架指标口径影响,需要通过实际对比测试确定最终验收阈值。

附录

附录A:核心公式

A.1 预热固定比例

prefix_len_i
=
align_down(
  input_len_i×target_ratio,
  block_size
)

A.2 0warmup请求级理论命中

hit_len_i
=
min(
  planned_prefix_len_i,
  cached_watermark_before_i
)

A.3 缓存水位更新

cached_watermark_after_i
=
max(
  cached_watermark_before_i,
  planned_prefix_len_i
)

A.4 全局理论命中率

designed_hit_ratio
=
sum(expected_hit_tokens)
/
sum(input_tokens)

A.5 最大Prefix长度

max_prefix_len_i
=
align_down(
  input_len_i-minimum_non_shared_length,
  block_size
)

A.6 数据构造偏差

construction_deviation
=
designed_hit_ratio-target_hit_ratio

A.7 运行实现偏差

runtime_deviation
=
actual_hit_rate-designed_hit_ratio

A.8 多DP全局命中率

global_hit_rate
=
sum(dp_hits_delta)
/
sum(dp_queries_delta)

附录B:数据Manifest示例

{
  "dataset_id": "progressive_multi_prefix_50",
  "generator_version": "1.0",
  "seed": 42,
  "tokenizer": "Qwen",
  "block_size": 16,
  "request_count": 1000,
  "execution_mode": "progressive_prefix_fill",
  "target": {
    "scope": "global",
    "hit_ratio": 0.5,
    "semantics": "order_aware_global"
  },
  "input_length": {
    "mode": "truncated_normal",
    "min": 1024,
    "max": 8192,
    "mean": 4096,
    "std": 1024
  },
  "prefix": {
    "mode": "multi_prefix",
    "group_count": 3,
    "minimum_non_shared_length": 16
  },
  "request_order": {
    "within_group": "input_len_asc",
    "across_group": "weighted_interleave"
  },
  "theoretical_result": {
    "target_hit_ratio": 0.5,
    "designed_hit_ratio": 0.4986,
    "signed_deviation": -0.0014,
    "absolute_deviation": 0.0014,
    "max_reachable_hit_ratio": 0.724,
    "validation_status": "PASS"
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions