执行摘要
- 一句话:使紧凑 MLA 行在不同 TP 大小下共享二级缓存路径
- 推荐动作:建议精读。此 PR 设计将 TP 无关 identity 的职责明确置于 FileMapper,通过单一 discriminator 字段隔离不同 layout,实现简洁清晰。测试覆盖充分,特别是跨 TP 字节级 round-trip 测试。合并后建议监控二级缓存命中率变化。
功能与动机
完成 #47929 中规划的 secondary-tier 步骤。#48906 使单节点 TP-only 的 MLA primary row 紧凑化,但 secondary-tier identity 仍包含 TP 配置(FS/OBJ 路径关联 TP 大小,P2P 兼容性指纹也基于相同字段)。本 PR 通过 replicated_layout 解耦 TP 身份,使紧凑行能在不同 TP 大小间共享二级缓存。
实现拆解
-
FileMapper 核心逻辑扩展(vllm/v1/kv_offload/file_mapper.py)
- __init__ 新增 replicated_layout: bool = False 参数,当为 True 时将 "replicated_layout": True 写入 fields(不写 False 以保持历史 hash 不变)。
- from_offloading_spec 中将 parallel_agnostic 的计算条件从 parallel_agnostic and parallel.is_parallelism_agnostic 扩展为 parallel_agnostic and (parallel.is_parallelism_agnostic or config.replicated_layout),使 replicated 行也能进入 parallel-agnostic 路径。同时传递 replicated_layout=(parallel_agnostic and config.replicated_layout) 给构造函数。
-
测试辅助函数参数化(tests/v1/kv_offload/test_file_mapper.py、test_fs_tier.py、test_obj_tier.py)
- make_mapper_from_offloading_spec、_make_offloading_spec、_make_offloading_config 新增 tp_size、rank、world_size、replicated_layout、is_parallelism_agnostic 参数,便于在各种并行配置下构造 FileMapper。
-
新增 FileMapper 单元测试(test_file_mapper.py:5 个测试)
- test_replicated_layout_collapses_parallel_identity:验证 TP=2 和 TP=4 的 replicated 行拥有相同的 base_path,且 rank/tp_size 等折叠为 1/0。
- test_replicated_layout_requires_caller_opt_in:未设置 parallel_agnostic=True 时 replicated 不起作用。
- test_non_replicated_keeps_parallel_identity:非 replicated 行保留原始 TP 身份。
- test_replicated_and_parallelism_agnostic_separate_layouts:replicated 与普通 parallel-agnostic 行具有不同 base_path。
- test_replicated_layout_run_config_tp_invariant:不同 TP 的 run_config 返回相同字典。
-
跨 TP 端到端 FS 测试(test_fs_tier.py:test_fs_tier_cross_tp_round_trip)
- 使用 TP=2 写入一个 block,再用 TP=4 读取,验证文件路径一致、数据字节级相等。
-
OBJ identity 折叠测试(test_obj_tier.py:test_obj_tier_replicated_layout_collapses_mapper_identity)
- 验证 TP=2 和 TP=4 的 replicated 配置下,ObjectStore tier 的 FileMapper base_path 和 run_config 完全一致。
关键文件:
vllm/v1/kv_offload/file_mapper.py(模块 文件映射器;类别 source;类型 core-logic;符号 FileMapper.init, FileMapper.from_offloading_spec): 核心实现文件,修改 FileMapper 的 __init__ 和 from_offloading_spec 以支持 replicated_layout,是 identity 折叠的关键入口。
tests/v1/kv_offload/test_file_mapper.py(模块 映射器测试;类别 test;类型 test-coverage;符号 test_replicated_layout_collapses_parallel_identity, test_replicated_layout_requires_caller_opt_in, test_non_replicated_keeps_parallel_identity, test_replicated_and_parallelism_agnostic_separate_layouts): 新增 5 个测试覆盖了 replicated_layout 的各种组合,确保 identity 折叠、opt-in 要求、与非 replicated 的隔离等。
tests/v1/kv_offload/tiering/test_fs_tier.py(模块 文件系统层;类别 test;类型 test-coverage;符号 _make_offloading_spec, test_fs_tier_cross_tp_round_trip): 端到端跨 TP 字节级 round-trip 测试,直接验证 FS tier 在 replicated_layout 下的正确性。
tests/v1/kv_offload/tiering/test_obj_tier.py(模块 对象存储层;类别 test;类型 test-coverage;符号 _make_offloading_config, test_obj_tier_replicated_layout_collapses_mapper_identity): 验证 OBJ tier 在 replicated_layout 下同样折叠 FileMapper identity,确保不同 TP 共享路径。
关键符号:FileMapper.init, FileMapper.from_offloading_spec
关键源码片段
vllm/v1/kv_offload/file_mapper.py
核心实现文件,修改 FileMapper 的 __init__ 和 from_offloading_spec 以支持 replicated_layout,是 identity 折叠的关键入口。
class FileMapper:
def __init__(
self,
root_dir: str,
model_name: str,
tokens_per_hash: int,
blocks_per_file: int,
tp_size: int,
pp_size: int,
pcp_size: int,
dcp_size: int,
rank: int,
dtype: str,
kv_cache_groups: list[dict] | None = None,
inference_engine: str = "vllm",
parallel_agnostic: bool = False,
replicated_layout: bool = False, # 新增参数
):
# 当 parallel_agnostic 为 True 时,折叠所有并行维度到 1 和 0
if parallel_agnostic:
tp_size = pp_size = pcp_size = dcp_size = 1
rank = 0
self.rank: int = rank
self.fields: dict = {
"model_name": model_name,
"tokens_per_hash": tokens_per_hash,
"blocks_per_file": blocks_per_file,
"tp_size": tp_size,
"pp_size": pp_size,
"pcp_size": pcp_size,
"dcp_size": dcp_size,
"dtype": str(dtype),
"kv_cache_groups": kv_cache_groups or [],
"inference_engine": inference_engine,
}
# 非 parallel_agnostic 时写入标记,确保旧版 hash 不变
if not parallel_agnostic:
self.fields["parallel_agnostic"] = False
# 仅在 replicated_layout 为 True 时写入,避免改变旧版 hash
if replicated_layout:
self.fields["replicated_layout"] = True
self.base_path: str = self._compute_base_path(root_dir, self.fields)
@classmethod
def from_offloading_spec(
cls,
root_dir: str,
offloading_spec: OffloadingSpec,
blocks_per_file: int = 1,
parallel_agnostic: bool = False,
) -> "FileMapper":
config = offloading_spec.config
parallel = config.parallel
# replicated_layout 行也被允许进入 parallel-agnostic 路径
return cls(
root_dir=root_dir,
model_name=config.model.name,
tokens_per_hash=config.cache.tokens_per_hash,
blocks_per_file=blocks_per_file,
tp_size=parallel.tp_size,
pp_size=parallel.pp_size,
pcp_size=parallel.pcp_size,
dcp_size=parallel.dcp_size,
rank=parallel.rank,
dtype=config.model.dtype,
kv_cache_groups=kv_cache_groups,
parallel_agnostic=(
parallel_agnostic
and (parallel.is_parallelism_agnostic or config.replicated_layout)
),
replicated_layout=(parallel_agnostic and config.replicated_layout),
)
tests/v1/kv_offload/test_file_mapper.py
新增 5 个测试覆盖了 replicated_layout 的各种组合,确保 identity 折叠、opt-in 要求、与非 replicated 的隔离等。
def test_replicated_layout_collapses_parallel_identity():
# 共享配置:replicated_layout=True + parallel_agnostic=True
shared = dict(
model_name="mla-model",
groups=((16, "mla_layer"),),
replicated_layout=True,
parallel_agnostic=True,
)
# TP=2 和 TP=4 应映射到相同路径
tp2 = make_mapper_from_offloading_spec(tp_size=2, world_size=2, rank=1, **shared)
tp4 = make_mapper_from_offloading_spec(tp_size=4, world_size=4, rank=3, **shared)
assert tp2.base_path == tp4.base_path
for fm in (tp2, tp4):
# 所有并行维度折叠为 1,rank 为 0
assert fm.fields["tp_size"] == 1
assert fm.fields["pp_size"] == 1
assert fm.fields["pcp_size"] == 1
assert fm.fields["dcp_size"] == 1
assert fm.rank == 0
# parallel_agnostic 字段不存在(因为已折叠),但 replicated_layout 保留
assert "parallel_agnostic" not in fm.fields
assert fm.fields["replicated_layout"] is True
# 文件名以折叠后的 _r0 开头
assert fm.get_file_name(make_offload_key(b"\x01" * 8, 0)).startswith(
f"{fm.base_path}_r0/"
)
tests/v1/kv_offload/tiering/test_fs_tier.py
端到端跨 TP 字节级 round-trip 测试,直接验证 FS tier 在 replicated_layout 下的正确性。
def test_fs_tier_cross_tp_round_trip(tmp_path):
"""TP=2 replicated writer 和 TP=4 reader 共享 namespace 和字节数据。"""
root = str(tmp_path)
writer_tensor = _page_aligned_rand_tensor(4, _BLOCK_ELEMENTS)
expected = writer_tensor[0].clone()
# 以 TP=2 写入
writer = FileSystemTierManager(
offloading_spec=_make_offloading_spec(
tp_size=2, world_size=2, rank=0, replicated_layout=True
),
primary_kv_view=memoryview(writer_tensor.numpy()),
tier_type="fs",
root_dir=root,
n_read_threads=2,
n_write_threads=2,
)
try:
writer.submit_store(make_job(1, [key(7)], [0]))
assert all(r.success for r in drain(writer))
writer_base = writer.file_mapper.base_path
writer_path = writer.file_mapper.get_file_name(key(7))
finally:
writer.shutdown()
reader_tensor = _page_aligned_zero_tensor(4, _BLOCK_ELEMENTS)
# 以 TP=4 读取,应命中同一路径
reader = FileSystemTierManager(
offloading_spec=_make_offloading_spec(
tp_size=4, world_size=4, rank=3, replicated_layout=True
),
primary_kv_view=memoryview(reader_tensor.numpy()),
tier_type="fs",
root_dir=root,
n_read_threads=2,
n_write_threads=2,
)
try:
assert reader.file_mapper.base_path == writer_base
assert reader.file_mapper.get_file_name(key(7)) == writer_path
assert lookup_and_wait(reader, [key(7)]) == [LookupResult.HIT]
reader.submit_load(make_job(2, [key(7)], [1], is_promotion=True))
assert all(r.success for r in drain(reader))
# 验证读取的数据与写入一致
assert torch.allclose(reader_tensor[1], expected)
finally:
reader.shutdown()
评论区精华
无实质性讨论。claude[bot] 自动评论(来自 fork 无法 review),orozery 直接批准。
风险与影响
关联脉络
- PR #49152 [KV-offload][FS] : Batch store/load_block in C: 同为 KV offload 二级缓存层优化,该 PR 为批量 IO 下沉,本 PR 为跨 TP 身份折叠,共同完善 secondary-tier 能力。
- PR #49947 [Bugfix][KV Offload][OBJ] Preserve job completion during cleanup: 影响 OBJ 层作业完成与清理,本 PR 的 OBJ 测试也验证了跨 TP 身份折叠,两者配合确保正确性。
参与讨论