Skip to content

[KVConnector] native scale-up KV connector (HIP VMM, kv_connector=native)#1499

Open
carlushuang wants to merge 4 commits into
mainfrom
carlushuang/xgmi-vmm-transfer
Open

[KVConnector] native scale-up KV connector (HIP VMM, kv_connector=native)#1499
carlushuang wants to merge 4 commits into
mainfrom
carlushuang/xgmi-vmm-transfer

Conversation

@carlushuang

@carlushuang carlushuang commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Native single-node KV connector (HIP VMM)

A fully in-tree prefill/decode KV connector for the single-node (scale-up / XGMI) case, using only the HIP Virtual Memory Management API — no MoRI, no Mooncake. Legacy hipIpc GPU-direct paths are unreliable cross-process here; VMM shareable handles are reliable and don't require the source GPU to be visible to the consumer (so natural device==rank, no reorder trick). Refs #1483.

What it adds

  • atom/kv_transfer/disaggregation/native/vmm.py (+ _vmm_ext.cpp): VmmBuffer (alloc / export_fd / import_fd / tensor) + copy. C++ helper is JIT-compiled lazily (never at import → CPU-CI safe).
  • native_connector.py: NativeConnector + NativeConnectorScheduler. Consumer sends a VMM staging fd over a UNIX socket; producer imports it, gathers the request's KV blocks + SWA slots + compressor state into it (hipMemcpy peer over XGMI); consumer scatters into its KV pool.
  • Registered as kv_connector="native" in the factory; native/README.md has the 4P4D launch recipe.
  • tests/test_native_vmm_transfer.py: cross-process transfer test (skips with < 2 GPUs / no VMM).

Usage

--kv-transfer-config '{"kv_connector":"native","kv_role":"kv_producer"|"kv_consumer",...}'

No protocol field (always XGMI); prefill HIP_VISIBLE_DEVICES=0,1,2,3 / decode 4,5,6,7 (no reorder).

E2E result — DeepSeek-V4-Pro 4P4D, no perf regression vs mooncake-hip

8×MI355X (gfx950), identical config (TP=4, util 0.85, --max-num-seqs 128, default chunk):

native-vmm mooncake-hip
serving c1 (out tok/s / TTFT / TPOT) 70.8 / 199 ms / 13.93 ms 71.0 / 204 ms / 13.88 ms
serving c16 780.6 / 313 ms / 18.75 ms 785.4 / 302 ms / 18.82 ms
prefill-iso c4 (req/s) 6.79 7.12
prefill-iso c16 (req/s) 13.21 13.37

Serving-level within ~0.5% (TPOT identical); prefill-isolation within 1–5% (native's staging gather/scatter overhead). Both use hipMemcpyPeer/XGMI. Native also ran c16 cleanly (no hang), unit test passes, black+ruff clean.

Scope / follow-ups

v1: requests transferred sequentially per step; follow-ups are a concurrent staging pool and batching the per-region copies (would close the small prefill-iso gap).

…nsfer

Adds a small VMM (Virtual Memory Management) helper that lets one process
export a GPU buffer as a POSIX fd and another process import it, grant its own
device peer access, and copy directly over the fabric (XGMI). This is the
transport foundation for a single-node (scale-up) prefill/decode KV connector.

Unlike legacy hipIpc handles, VMM shareable handles + explicit hipMemSetAccess
give reliable cross-process peer access at high concurrency and do not require
the source GPU to be in the consumer's visible set (so prefill/decode keep
natural device==rank placement; no reorder or base_gpu_id changes needed).

- atom/kv_transfer/disaggregation/xgmi/{__init__,vmm}.py: VmmBuffer API
  (alloc / export_fd / import_fd / tensor). The tiny C++ helper is
  JIT-compiled lazily on first use (never at import time), so importing the
  module is cheap and safe on CPU-only hosts.
- tests/test_xgmi_vmm_transfer.py: cross-process test (producer on GPU0
  exports, consumer on GPU1 imports, peer-copies and verifies). Skips with
  fewer than 2 GPUs or no VMM support.

Validated on 8xMI355X (gfx950, ROCm 7.2.4): cross-process peer copy is correct
at high concurrency where legacy hipIpc corrupts/faults.

Refs #1483
Rename the xgmi/ package to native/ (it depends on no third-party transport)
and wire the HIP VMM transport primitive into a registered KVConnector.

- atom/kv_transfer/disaggregation/native/native_connector.py:
  NativeConnector + NativeConnectorScheduler. Scheduler is transport-agnostic
  (transfer_id<->request_id, ConnectorMetadata). Worker uses a VMM staging
  buffer + direct hipMemcpy peer copies over XGMI: the consumer exports a VMM
  staging fd over a UNIX side channel; the producer imports it, gathers the
  request's KV blocks straight into it, and the consumer scatters into its KV
  pool. No RDMA, no IPC-handle churn, no host staging.
- factory.py: register "native".
- native/README.md: when to use it, selection, and the 4P4D launch recipe
  (no "protocol" field, natural device==rank placement).
- _vmm_ext.cpp / vmm.py: add vmm_ptr + vmm_copy (raw peer DtoD copy).

Users select it with:
  --kv-transfer-config '{"kv_connector":"native","kv_role":"kv_producer"|"kv_consumer",...}'

Scope: v1 wires the VMM primitive (validated cross-process by
tests/test_native_vmm_transfer.py) into the connector interface; requests in a
step are transferred sequentially and the DeepSeek-V4 slot/index-region fast
paths + a concurrent staging pool are follow-ups. End-to-end 4P4D serving
validation is tracked in #1483.

Refs #1483
@carlushuang carlushuang marked this pull request as draft July 7, 2026 10:30
@carlushuang carlushuang changed the title feat(kv): HIP VMM cross-process buffer sharing for single-node KV transfer feat(kv): native single-node KV connector (HIP VMM, kv_connector=native) Jul 7, 2026
@carlushuang carlushuang changed the title feat(kv): native single-node KV connector (HIP VMM, kv_connector=native) [KVConnector] native scale-up KV connector (HIP VMM, kv_connector=native) Jul 7, 2026
The producer must cache reqs_to_save under the request seq.id (the transfer
handle the consumer requests, per request_finished), not params["transfer_id"]
(which defaults to 0). Symptom before the fix: the first request succeeded
(seq.id 0 == default 0) but subsequent requests timed out waiting for the
prefill entry, then gathered nothing.

Validated end-to-end on 8xMI355X, DeepSeek-V4-Pro, single-node 4P4D
(kv_connector=native): correct output, and no meaningful perf regression vs
mooncake protocol=hip under identical config (TP=4, util 0.85, max_num_seqs
128). Serving (ISL/OSL=1024) within ~0.5%; prefill-isolation within 1-5%
(staging gather/scatter overhead). Both use hipMemcpyPeer over XGMI.
@carlushuang carlushuang marked this pull request as ready for review July 7, 2026 11:19
@carlushuang

Copy link
Copy Markdown
Collaborator Author

E2E DSV4 4P4D validated — no perf regression vs mooncake-hip

Brought up single-node 4P4D on 8×MI355X (gfx950) with kv_connector=native (DeepSeek-V4-Pro, TP=4 prefill + TP=4 decode). Correct output via the proxy (do_remote_prefill path), then benchmarked against mooncake protocol=hip under identical config (util 0.85, --max-num-seqs 128, default chunk).

Prefill-isolation (ISL=1024/OSL=16), req/s | median TTFT:

conc native-vmm mooncake-hip
1 2.42 / 197 ms 2.44 / 199 ms
4 6.79 / 395 ms 7.12 / 387 ms
8 10.26 / 568 ms 10.49 / 575 ms
16 13.21 / 939 ms 13.37 / 955 ms

Real serving (ISL/OSL=1024), out tok/s | TTFT | TPOT:

conc native-vmm mooncake-hip
1 70.8 / 199 ms / 13.93 ms 71.0 / 204 ms / 13.88 ms
16 780.6 / 313 ms / 18.75 ms 785.4 / 302 ms / 18.82 ms

No meaningful regression: serving-level within ~0.5% (TPOT identical); prefill-isolation within 1–5% (native's staging gather/scatter overhead — 91 block + 62 slot + compressor-state copies/req vs mooncake's batched transfer). Both use hipMemcpyPeer over XGMI, so parity is expected.

Notes: native ran c16 cleanly (no hang), used disjoint visibility (no reorder trick), and needs no third-party module. A live fix went in for the transfer_id keying (cache under seq.id). Remaining v1 follow-ups: sequential per-step transfers → concurrent staging pool, and batching the many small per-region copies.

…xgmi-vmm-transfer

# Conflicts:
#	atom/kv_transfer/disaggregation/factory.py
@zufayu zufayu requested a review from ZhangLirong-amd July 8, 2026 02:15
self._prefills_cv.wait_for(
lambda: transfer_id in self._prefills, timeout=_PREFILL_WAIT_S
)
src_block_ids, src_slot = self._prefills.get(transfer_id, ([], -1))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

memory leak risk, after finish, self._prefills should pop,

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants