Skip to content

[KV Offload] Enable single-copy MLA layout for CPUOffloadingSpec - #50301

Merged
orozery merged 6 commits into
vllm-project:mainfrom
Change72:kv-offload-default-mla-dedup
Jul 31, 2026
Merged

[KV Offload] Enable single-copy MLA layout for CPUOffloadingSpec#50301
orozery merged 6 commits into
vllm-project:mainfrom
Change72:kv-offload-default-mla-dedup

Conversation

@Change72

@Change72 Change72 commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Purpose

Enable the single-copy (replicated) MLA layout for the default KV-offload backend CPUOffloadingSpec, closing out the plan in #47929. After #50094 moved the default backend's worker CPU buffer onto the shared SharedOffloadRegion (on CUDA/ROCm), the default backend can now deduplicate the per-TP-rank-replicated MLA KV the same way TieringOffloadingSpec already does — but only on deployments that actually allocate on the shared region.

MLA latent KV is logically replicated across TP ranks (expected to be byte-identical in homogeneous TP without context parallelism), so storing one copy instead of world_size copies is safe under the existing replicated_layout gate (pure-MLA single group, TP>1, PP=PCP=DCP=1, world==tp, single-node mp).

Maintainer context (upstream reviewers do not see the issue thread):

What changes

  • replicated_layout is gated by a new per-spec hook _uses_shared_region()current_platform.is_cuda_alike() for the default spec, True for TieringOffloadingSpec. It is the single source of truth used by both the replicated-layout sizing (num_copies) and the create_worker allocation path, so the two cannot drift. This replaces the static SUPPORTS_REPLICATED_LAYOUT capability flag, which is removed: now that the default spec can deduplicate on the shared region, that flag was True for every in-tree spec and carried no information.
  • CPUOffloadingSpec.create_worker now maps every rank to slot 0 under replicated layout (single MLA copy) and otherwise folds the physical device index into [0, world_size) — mirroring TieringOffloadingSpec.create_worker.
  • TieringOffloadingSpec overrides _uses_shared_region() to True (it always allocates on the shared region, on every platform), so its behavior is unchanged.

Why the platform gate is load-bearing (fail-closed)

The replicated_layout config gate has no platform clause, and #50094 intentionally kept non-CUDA-alike platforms (currently XPU) on a per-rank private pinned tensor. Enabling dedup there would be a correctness bug, not a perf trade-off: the connector's rank-0 writer gate (offloading/worker.py, not spec.replicated_layout or rank == 0) would ack rank>0 stores without performing the D2H copy, leaving those private buffers empty and corrupting later loads. Gating the flip on _uses_shared_region() makes non-shared-region deployments fail closed to the existing private, per-rank layout; a dedicated test pins this.

Behavior changes

  • Replicated-MLA deployments on the default backend: one host copy instead of world_size — cache capacity scales ×TP for the same cpu_bytes_to_use, and D2H store traffic drops to 1/TP (only rank 0 writes). Store-throughput metrics become writer-only, same as the tiering spec's existing disclosure.
  • Non-CUDA-alike platforms: unchanged (private per-rank tensor, per-rank sizing).
  • TieringOffloadingSpec: unchanged.
  • Deduplication rests on the existing gate's premise that the per-rank MLA KV is logically replicated (expected byte-identical in homogeneous TP without context parallelism) — the same assumption TieringOffloadingSpec already relies on; this PR stores one copy instead of world_size. The default backend has no persistent tier, so there is no cross-version on-disk compatibility concern.

Not in this PR

Duplicate-work check

Searched open PRs (SUPPORTS_REPLICATED_LAYOUT, CPUOffloadingSpec replicated, 47929 in:body) and the touched-path history — no open PR enables dedup for the default CPU offload backend. Nearest neighbor: #48414 (@Etelis) works the parallelism-agnostic canonical allocation layout (the #50094 area); it does not touch the replicated-layout gate, so there is no overlap with this PR.

Test Plan

# Unit (offloading spec gating + sizing + create_worker)
.venv/bin/python -m pytest tests/v1/kv_offload/test_factory.py -q
pre-commit run --files vllm/v1/kv_offload/cpu/spec.py vllm/v1/kv_offload/tiering/spec.py tests/v1/kv_offload/test_factory.py

A100 TP=2 end-to-end (MLA model to exercise replicated_layout): serve DeepSeek-V2-Lite with -tp 2 and the OffloadingConnector, before (no dedup) vs after (dedup), and compare the connector's D2H store bytes and greedy output.

Test Result

Unit (NVIDIA L4, torch 2.13.0+cu130): tests/v1/kv_offload/test_factory.py38 passed. Covers the platform × config truth matrix, shared-region single-copy sizing (num_copies=1), the non-shared-region data-loss guard (replicated stays off, per-rank sizing preserved), and the replicated create_worker (rank 0). pre-commit (ruff, ruff-format, mypy, SPDX, torch.cuda-API guard) all pass.

A100 TP=2 E2E (2× A100-SXM4-80GB, DeepSeek-V2-Lite, --load-format dummy --seed 0 -tp 2, cpu_bytes_to_use=4 GiB, image vllm/vllm-openai:nightly-6f91edf96d whose v1/kv_offload/ is byte-identical to this PR's base — only the two changed spec files were overlaid for the "after" run):

connector D2H store_bytes_total greedy output
before (no dedup, world_size copies) 24.8832 MB reference
after (dedup, single copy) 12.4416 MB identical

The store bytes drop to exactly 1/TP — only rank 0 performs the D2H copy under the writer gate — confirming replicated_layout engages for the default backend on an MLA model. Greedy generation is unchanged for this deterministic dummy-weights run. (This run exercises the D2H store side; it does not force a CPU→GPU reload and is not itself a per-rank KV byte-equality proof.) The capacity ×TP effect is the num_copies=1 sizing, covered by the unit tests; the byte-exact GPU↔shared-region transfer is covered by #50094's test_transfer[use_shared_memory=True] and the tiering suite.


This PR was developed with AI assistance (implementation and tests). Unit tests and lint were run on the submitter's NVIDIA L4 workstation; the A100 TP=2 E2E was run on 2× A100-80GB.

After vllm-project#50094 moved the default CPUOffloadingSpec worker buffer onto the shared
SharedOffloadRegion (CUDA-alike only), enable the same single-copy replicated
MLA layout the tiering spec already uses -- but only on deployments that
actually allocate on the shared region.

The config replicated_layout gate has no platform clause, and non-CUDA-alike
platforms keep a per-rank private pinned tensor. Enabling dedup there would let
the connector's rank-0 writer gate ack rank>0 stores without writing, leaving
those private buffers empty (data loss). So the flip is gated by
_uses_shared_region() -- the single source of truth shared by both the
replicated-layout sizing and the create_worker allocation path, so the two
cannot drift. TieringOffloadingSpec overrides it to True (it always allocates
the shared region), leaving its behavior unchanged.

Co-authored-by: Cursor Agent <cursor-agent@cursor.com>
Signed-off-by: Change72 <changg@nvidia.com>
@mergify mergify Bot added the v1 label Jul 29, 2026
With the default CPUOffloadingSpec now able to deduplicate on the shared
region, the SUPPORTS_REPLICATED_LAYOUT class attribute is True for every
in-tree spec, so ANDing it into the replicated_layout gate carried no
information. Gate solely on _uses_shared_region() -- the per-spec predicate
that already distinguishes "allocates on the shared region" (CPU: CUDA-alike;
tiering: always) -- and remove the flag from both specs.

Co-authored-by: Cursor Agent <cursor-agent@cursor.com>
Signed-off-by: Change72 <changg@nvidia.com>
@Change72
Change72 force-pushed the kv-offload-default-mla-dedup branch from 6fcc473 to b0a7a43 Compare July 29, 2026 17:30
@Change72
Change72 marked this pull request as ready for review July 29, 2026 17:53
@Change72
Change72 requested review from ApostaC and orozery as code owners July 29, 2026 17:53

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

Comment thread vllm/v1/kv_offload/cpu/spec.py Outdated
Comment on lines +145 to +152
"""Whether this deployment backs the worker CPU buffer with the shared
mmap region rather than a per-rank private pinned tensor.

Single source of truth for both the replicated-layout sizing gate and
the ``create_worker`` allocation path: deduplication (single MLA copy +
rank-0 writer gate) is only safe where a shared medium actually exists,
so non-CUDA-alike platforms (private-tensor path) must never enable it.
"""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we minimize this comment?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 3161201.

Comment thread tests/v1/kv_offload/test_factory.py Outdated


@pytest.mark.parametrize("world_size", [2, 4, 8])
def test_cpu_spec_replicated_config_ignored_off_shared_region(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude suggests this name instead test_cpu_spec_replicated_disabled_without_shared_region

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 3d396f3.

Change72 and others added 2 commits July 29, 2026 23:40
Per review on vllm-project#50301: minimize the create_worker rank-selection comment, and
rename test_cpu_spec_replicated_config_ignored_off_shared_region to
test_cpu_spec_replicated_disabled_without_shared_region.

Co-authored-by: Cursor Agent <cursor-agent@cursor.com>
Signed-off-by: Change72 <changg@nvidia.com>
Trim the verbose docstring orozery flagged on vllm-project#50301 to the essentials; the
full data-loss rationale for the platform gate lives in the PR description and
the replicated-disabled-without-shared-region test.

Co-authored-by: Cursor Agent <cursor-agent@cursor.com>
Signed-off-by: Change72 <changg@nvidia.com>
Comment thread tests/v1/kv_offload/test_factory.py Outdated
assert worker_calls[0]["mmap_region"] is None


def test_cpu_spec_create_worker_uses_single_slot_for_replicated_layout(monkeypatch):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude claims this test can be made a bit less fragile:

@pytest.mark.parametrize(
    "replicated_layout, device_index, world_size, expected_rank",
    [
        (True, 5, 4, 0),       # replicated: always slot 0
        (True, 0, 4, 0),       # replicated: slot 0 regardless of device
        (False, 5, 4, 1),      # non-replicated: 5 % 4 = 1
        (False, 7, 4, 3),      # non-replicated: 7 % 4 = 3
    ],
)
def test_cpu_spec_create_worker_rank_assignment(
    monkeypatch, replicated_layout, device_index, world_size, expected_rank
):
    import vllm.v1.kv_offload.cpu.spec as cpu_spec_module

    monkeypatch.setattr(cpu_spec_module.current_platform, "is_cuda_alike", lambda: True)
    worker_kv_bytes_per_block = SharedOffloadRegion.BLOCK_SIZE_ALIGNMENT
    spec = _create_spec(
        cpu_bytes_to_use=worker_kv_bytes_per_block * 8,
        worker_kv_bytes_per_block=worker_kv_bytes_per_block,
        world_size=world_size,
        replicated_layout=replicated_layout,
    )

    region_calls: list[dict[str, Any]] = []

    monkeypatch.setattr(
        cpu_spec_module, "SharedOffloadRegion", lambda **kw: region_calls.append(kw) or MagicMock()
    )
    monkeypatch.setattr(cpu_spec_module, "CPUOffloadingWorker", lambda **kw: MagicMock())
    monkeypatch.setattr(
        cpu_spec_module.torch.accelerator, "current_device_index", lambda: device_index
    )

    spec.create_worker(MagicMock())

    assert region_calls[0]["rank"] == expected_rank

What this fixes:

  1. Asserts only the behavioral delta — rank is the only assertion. No kv_bytes_per_block (tested elsewhere), no mmap_region is region (trivial wiring).
  2. Tests both branches — parametrizes replicated vs non-replicated, proving the if/else works correctly. This closes the gap I identified earlier (the "else" branch was untested after the refactor).
  3. Descriptive parametrize IDs — when this test fails, the parametrize label tells you immediately whether it's the replicated path or the non-replicated path that broke.
  4. Single assertion per concern — the test proves exactly one thing: "given (replicated_layout, device_index, world_size), the rank passed to SharedOffloadRegion is X." One assertion, one failure mode.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Replace the replicated-only rank test with a parametrized
test_cpu_spec_create_worker_rank_assignment covering both the replicated
(slot 0) and device-fold branches plus edge cases, asserting only the rank
(the behavioral delta). Drop the now-redundant rank assertion from the
mmap-wiring test.

Co-authored-by: Cursor Agent <cursor-agent@cursor.com>
Signed-off-by: Change72 <changg@nvidia.com>
@orozery orozery added the ready ONLY add when PR is ready to merge/full CI is needed label Jul 30, 2026

@orozery orozery left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Change72 !

@orozery
orozery merged commit 541128b into vllm-project:main Jul 31, 2026
85 checks passed
@Change72
Change72 deleted the kv-offload-default-mla-dedup branch July 31, 2026 03:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready ONLY add when PR is ready to merge/full CI is needed v1

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants