Skip to content

[Feature]: Deduplicate replicated MLA KV across TP ranks in native offloadingΒ #47929

Description

@Change72

πŸš€ The feature, motivation and pitch

Problem

For MLA models, the latent KV cache is logically replicated across TP ranks β€” and expected to be byte-identical in homogeneous TP setups without context parallelism. MLA hardcodes num_kv_heads=1 (vllm/model_executor/layers/attention/mla_attention.py:385) and the latent projection is not TP-sharded (ReplicatedLinear, or disable_tp=True in the fused-projection paths). The codebase states this directly:

"MLA is always replicated as the hidden dim can't be split" β€” vllm/distributed/kv_transfer/kv_connector/utils.py:562

The native KV offloading path (OffloadingConnector + vllm/v1/kv_offload/) does not exploit this. In the pure-MLA, DCP=PCP=1 baseline configuration (refs against main @ 7bd154375):

  • Native CPU/tiering offload materializes one copy of the logically replicated latent KV per TP worker. CPUOffloadingSpec prices each logical block at per-worker bytes Γ— world size (vllm/v1/kv_offload/cpu/spec.py:86), so at TP=N the unique-content capacity is roughly 1/N of the configured --kv-offloading-size budget.
  • Default CPUOffloadingSpec: each rank allocates its own private CPU buffer (pinned when supported) (vllm/v1/kv_offload/cpu/gpu_worker.py:501).
  • TieringOffloadingSpec: on single-node TP, the copies live in different per-worker slots of the same shared-mmap row (worker0_block0 | worker1_block0 | ..., vllm/v1/kv_offload/cpu/shared_offload_region.py:123); the FS secondary tier then writes the entire row as one layout-specific block file per key, so the duplicate copies sit inside that file as distinct worker slots (vllm/v1/kv_offload/tiering/fs/manager.py:159). These observations are verified for single-node TP; multi-node tiering β€” where both /dev/shm and the scheduler's memory view are node-local β€” is treated as unsupported/unverified here.
  • The fs tier's parallel-agnostic mode acknowledges the replication but excludes MLA from it: "MLA is excluded: its latent KV is replicated per rank, never head-sharded" (vllm/v1/kv_offload/file_mapper.py:85, from [KV offload] Parallel-agnostic fs-tier cache for single full-attention groupΒ #44733). Note that relaxing this predicate alone would not be enough β€” because the file payload is the full multi-worker row, files produced under different TP sizes differ in length and layout; a compact, TP-independent payload is a prerequisite.

Net effect: --kv-offloading-size 100 (GiB) with TP=8 on a pure-MLA model yields only ~12.5 GiB of unique cached KV. Neither the kv_offloading_size docstring ("When TP > 1, this is the total buffer size summed across all TP ranks", vllm/config/cache.py:182) nor docs/features/kv_offloading_usage.md mentions this caveat.

MLA is the best case for deduplication (replicated content, no reshard needed), yet it is the one case excluded from the existing single-copy machinery.

Scope and correctness notes

  • In general the duplication factor for pure-MLA is about TP / DCP, not always TP: DCP shards tokens across context-parallel ranks, but each DCP shard is still replicated across the TP ranks that share it. PCP is outer to TP, so TP-dedup can in principle be applied within each PCP namespace independently.
  • world_size also includes PP, and PP stages hold different layers β€” that dimension is real sharding, not replication, and must remain untouched by any dedup.
  • Proposed initial scope: pure-MLA models with PP=PCP=DCP=1 β€” a safe MVP boundary where the replica factor is exactly TP; this is an implementation simplification, not a theoretical requirement. Extensions (DCP-aware factor, per-PCP-namespace dedup, PP composition) can follow.
  • Hybrid MLA + full-attention models may place both kinds of layers in the same KV cache group, so gating purely at group level is insufficient; generalization needs per-layer/region (canonical tensor) granularity. The initial version would simply not engage for hybrid models.
  • Scheduler-side block accounting (num_cpu_blocks, medium/events) must stay consistent with the single-copy worker-side layout.

Prior art

  • MooncakeStoreConnector ([KV Transfer] Add MooncakeStoreConnector for KV cache offloading via Mooncake distributed storeΒ #40900) implements exactly this storage dedup: with num_kv_head < tp_size (MLA β‡’ 1) and dcp_size <= 1, TP ranks collapse to one shared key namespace and stripe PUTs across ranks so each block is stored once (vllm/distributed/kv_transfer/kv_connector/v1/mooncake/store/worker.py:1004). The DCP guard was added in [Bugfix][KV Connector] Disable Mooncake TP put-striding when DCP > 1Β #45371; PCP needs no guard since it is outer to TP.
  • LMCache (LMCacheConnectorV1): for MLA, save_only_first_rank defaults on β€” non-leader ranks neither store nor read; the leader retrieves and performs a TP-group device broadcast (v0.5.1 cache_engine.py L113-117). Cache keys collapse world_size to 1 (TP-size-agnostic) since LMCache#2098 (first released in v0.3.10). A separate remote-only scheme, remote_enable_mla_worker_id_as0, serves as an alternative when save_only_first_rank is disabled (each rank reads the single rank-0 copy from remote storage directly). The built-in single-server LMCacheMPConnector fallback collapses only the TP dimension and suppresses MLA stores on ranks where rank % tp_size != 0 ("Tensor parallel does not change the KV caches for MLA models", vllm/distributed/kv_transfer/kv_connector/v1/lmcache_mp_connector.py); in a TP-only deployment this yields one stored copy while every TP rank retrieves directly. The external connector preferred by current vLLM has separate multi-server writer semantics.
  • (The NIXL connector's single-source MLA handling is a transfer optimization for replicated KV, not storage dedup β€” noted only to delimit scope.)

The native CPU/tiering layout currently lacks an equivalent MLA replica-reduction path.

Proposed direction (candidates β€” feedback requested)

  1. Sizing and single-copy layout must land together. The per-block cost in cpu/spec.py should drop only the confirmed TP replica factor, leaving PP (different layers per stage) and any context-parallel sharding intact. Adjusting the sizing alone is unsafe: with per-worker private buffers still in place, simply growing num_cpu_blocks would multiply actual memory use by roughly world_size beyond the configured budget β€” the single-copy physical layout is a prerequisite for the sizing change.
  2. Candidate A β€” single-node tiering shared-mmap single slot: store one worker slot per block row; the TP leader performs the d2h write, all node-local ranks read the same slot. Open questions to resolve: store-completion synchronization across ranks, pinning and ownership of the shared region, row-stride/layout change, and the fact that /dev/shm is node-local β€” multi-node TP cannot share the slice without communication.
  3. Candidate B β€” leader store + TP-group broadcast on load (LMCache-style): works uniformly across single- and multi-node TP; adds TP-group broadcast traffic on load.
  4. FS tier: produce a compact, TP-independent payload (single logical copy) and then admit MLA into the parallel-agnostic naming of file_mapper.py. Note that this naming mode erases all parallel dimensions (TP/PP/DCP/PCP) and rank, not just TP ("tp/pp/pcp/dcp are forced to 1 and rank to 0", vllm/v1/kv_offload/file_mapper.py:43), so the admission stays gated to the PP=PCP=DCP=1 MVP; extending beyond that requires TP-only naming or canonicalizing the remaining axes first. This aligns naturally with the canonical KV layout direction in [KV Connector] Add canonical KV layout fields for TP-agnostic offloadΒ #46954.

Happy to converge on whichever direction fits the multi-tier design; a hybrid (A for node-local tiers, B for the default spec) is also plausible.

Coordination

This slots under RFC #38260 (its "canonical TP1 form" principle implies exactly one MLA copy in storage) and relates to #46954 (canonical KV layout fields β€” MLA latent modeled head-less) and #44865 (transfer data model refactor touching the same paths).

Not a duplicate of #38395 ("Fix/mla kv offloading", open draft): that patch keeps the world_size multiplication and per-TP-rank copies β€” it addresses MLA/HMA correctness and early disk-tier experimentation, not cross-rank replica dedup.

cc @orozery @Etelis @dannyharnik

Alternatives

Additional context

Duplicate-work check: searched open/closed issues and PRs (keyword sweeps, vllm/v1/kv_offload/ git history, and the comment threads of #19854 / #38260 / #33689). No existing issue or PR implements native MLA TP replica dedup. Closest items: #44733 (explicitly excludes MLA from parallel-agnostic caching), #46954 (layout schema only, no write-side dedup semantics), #44865 (overlapping-path refactor), #38395 (see above β€” retains per-rank copies).

I intend to follow up with a PR implementing the initial scope above, pending feedback on the preferred direction.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions