Repository navigation
Conversation
8dc54c0 to
3d44d3a
Compare
|
@yuxianq, could you help to review this PR as trt-llm-torch-attention-devs? Thanks~ Assignment explanation
Affinity and workload are snapshots at selection, not live counters. Existing reservations on this PR are counted once. |
55b71f3 to
00f2d69
Compare
|
/bot run |
|
PR_Github #80686 [ run ] triggered by Bot. Commit: |
|
PR_Github #80686 [ run ] completed with state
|
00f2d69 to
17b79ba
Compare
| runtime.fork() | ||
| try: | ||
| with _locality_domain_metadata(metadata, ld_slice, | ||
| locality_domain_id): |
There was a problem hiding this comment.
Could we give each locality-domain call its own CuTe DSL MLA split-plan tensor and cache key, or keep that backend on a single stream? On Rubin the default selector can choose CuteDslMlaFmha, for example H=96 FP8 decode. Its _split_kv_plan() keeps one _cute_dsl_mla_split_plan on this shared metadata. Both iterations here launch plan writers and MLA readers against it on separate streams before runtime.join(). With different KV lengths, domain 1 can overwrite split counts while domain 0's attention/reduction still reads them, producing incorrect output. The per-domain workspace overrides do not isolate that tensor. Please cover unequal per-domain KV lengths with the CuTe DSL backend and concurrent streams.
There was a problem hiding this comment.
Thanks, Bowen! Fixed in 79391d2: each domain now keeps its own split plan and key. test_cute_dsl_mla_decode_with_unequal_domain_lengths covers it and passes on Rubin.
|
|
||
| def pick_locality_domain( | ||
| self, | ||
| request_id: int, |
There was a problem hiding this comment.
Why we still pass request_id? It is unused after you switch to arrival-order round-robin method.
…on and resize in the C++ KV cache manager v2 On a GPU with locality domains (Rubin), each domain's KV cache lives in its own physical memory. The C++ KVCacheManagerV2 now keeps one physical chunk pool per domain, mapped at explicit offsets of one VA reservation per pool, so slot ids stay global and base + slotSize * id is unchanged for every consumer. - Allocation, eviction, migration, resize and quota are per domain; a domain's share of the quota is taken from the raw quota on init and resize. - ReuseScope carries the domain, so prefix reuse stays within a domain. - LocalityDomainId is a StrongIndex like SlotId. - A domain chunk must start on a physical-page boundary in every pool; when the slot sizes need an oversized chunk the manager keeps one domain. - Whole-pool consumers get the mapped ranges; the per-domain free-slot count is an introspection hook for tests. - TRT_LLM_MOCK_LOCALIZATION_SUPPORT=1 splits the cache on any GPU for testing. Signed-off-by: Faraz Khoubsirat <58580514+farazkh80@users.noreply.github.com>
…est path enable_locality_domains builds a localized KVCacheManagerV2. Each new request is placed on a domain round-robin over arrival order, carries it through every KV cache creation, probe and prefetch, and child requests inherit it. Sparse attention models are rejected at manager creation when the flag is set, and mock localization keeps attention on one stream. Signed-off-by: Faraz Khoubsirat <58580514+farazkh80@users.noreply.github.com>
…stream With locality domains, attention forks one call per domain onto the domain's Green Context stream, with metadata sliced to that domain's requests, and joins afterwards. The decoder runner groups generation rows by domain (keeping the overlap scheduler's host-token rows first), splits by ownership when the rows are domain-sorted and by position otherwise, and builds per-domain warmup and CUDA graph dummies. Per-domain MLA decode state uses one counter allocation and reuses domain 0's sequence offsets. Speculative decoding slices its per-domain offsets. Partitioned attention rejects FlashMLA, beam search, cross attention, helix, custom masks, sparse indices and MRoPE outside the mixed input type. Signed-off-by: Faraz Khoubsirat <58580514+farazkh80@users.noreply.github.com>
… locality domain A kernel launched on a locality-domain stream runs only on that Green Context's SMs, but MLA decode picked the trtllm-gen kernel and sized its KV split for the whole device, launching about twice the CTAs one wave could hold. The localization handle now records the SM count of every stream it creates, and MLA generation passes it to the FMHA runner in place of the device count. The lookup is a table read, so it is safe during CUDA graph capture. GQA decode keeps the device count: at batch 1-16 its kernels ran 5-10% slower with it. Signed-off-by: Faraz Khoubsirat <58580514+farazkh80@users.noreply.github.com>
A localized pool is backed only on its domains' mapped ranges, so the transceiver registers each backed range of a pool rather than the whole extent, and the page table carries the ranges to the peer. The single-process transceiver test transfers between localized and non-localized pools, MHA and MLA, at TP 1->1, 2->1 and 1->2, with a head size that leaves a hole in the pool. Signed-off-by: Faraz Khoubsirat <58580514+farazkh80@users.noreply.github.com>
Explains what a locality domain is, when enabling it helps (dense GQA and MLA decode attention at high batch or concurrency) and when to leave it off (low concurrency, small decode batches, sparse attention), with the config snippet. Signed-off-by: Faraz Khoubsirat <58580514+farazkh80@users.noreply.github.com>
…KVCM2 Each Slot stored its locality domain next to its slot id, and every page rebuild and release copied or checked it, so the two could disagree. The slot id already decides the domain: each chunk of the shared slot-id space is mapped from exactly one domain's memory. GpuPoolGroup now keeps a per-chunk owner table, updated when a chunk is assigned to or returned from a domain, and answers localityDomainOf(slotId). Page::localityDomainId() and release use that lookup; Slot no longer carries a domain, and release no longer needs it to find the issuing allocator. Signed-off-by: Faraz Khoubsirat <58580514+farazkh80@users.noreply.github.com>
…ir cached prefix Prefixes are reused only within a locality domain, so round-robin placement sent about half of a conversation's follow-up turns to the domain that had not cached its history, and those turns recomputed it. pick_locality_domain now probes each domain's radix tree with the prompt and picks the domain with the longest cached prefix when the match covers at least one block and that domain does not carry clearly more live KV caches than the others (more than max(8, a quarter of the total)). Otherwise it falls back to round-robin. On a 64-conversation, 4-turn chat with Qwen3-4B and locality domains on, the share of prompt tokens served from cache on turns 2-4 goes from 65.8% to 94.8%, and those turns run 2.1x faster. Signed-off-by: Faraz Khoubsirat <58580514+farazkh80@users.noreply.github.com>
…n its domain count differs from the target's The target and a separate draft KV cache manager each fall back to one domain on their own slot sizes and quota, so their domain counts can differ. The target places the request, and the draft manager now maps that placement into its own domains: a one-domain draft ignores the id, and a two-domain draft under a one-domain target picks a stable domain from the request id. The draft sub-forward runs unpartitioned in both cases. Signed-off-by: Faraz Khoubsirat <58580514+farazkh80@users.noreply.github.com>
CuTe DSL MLA decode caches its per-request split-KV plan on the attention metadata and relaunches the plan writer when its key changes. The per-domain calls slice the KV lengths, so each domain's key differs and domain 1's writer rewrote the shared plan on its stream while domain 0's kernels could still read it. Each domain now keeps its own plan and key on the metadata, swapped in for its call and reset with the shared key. Signed-off-by: Faraz Khoubsirat <58580514+farazkh80@users.noreply.github.com>
Placement is round-robin over arrival order or follows the cached prefix, so the request id is no longer an input. Signed-off-by: Faraz Khoubsirat <58580514+farazkh80@users.noreply.github.com>
17b79ba to
83d406e
Compare
|
/bot run |
|
Could we make matching target/draft locality-domain counts an initialization invariant? This is not required for this PR; we can also address it in a follow-up PR if that makes sense. Currently, each Suggested approach:
The allocation code suggests that removing the chunk-size fallback may not require slot padding: For the configuration-derived DeepSeek example—61 target layers, one draft layer, 61 GiB/1 GiB quotas—the existing formulas give 14,563 slots per domain for both managers with two domains. Target backing is 30.5 GiB per domain despite its 17,568 MiB chunk extent. This arithmetic has not been validated in an end-to-end GPU run. Regression coverage should include this asymmetric layout, multiple pools with small scale buffers, growth/shrink across chunk boundaries, and request IDs |
|
PR_Github #81190 [ run ] triggered by Bot. Commit: |
|
PR_Github #81190 [ run ] completed with state
|
Locality domain (LD) support for the KV cache on the C++ KVCacheManagerV2, plus two-stream attention. Supersedes #18919 and #19336.
Description
A locality domain is one half of a Rubin GPU with its own HBM. With
enable_locality_domains(default off), a request's KV cache lives in one domain and its attention runs on that domain's SMs.base + slotSize * idstay global. Allocation, eviction, migration, resize and quota are per domain; a page's domain follows from its slot id, and the domain id joins the reuse key. Disaggregated serving transfers each domain's pool.docs/source/features/locality-domains.mdsays when to enable it.TRT_LLM_MOCK_LOCALIZATION_SUPPORT=1splits the KV cache on any GPU without forking streams.Limits. Prefix reuse is per domain; a request whose prefix sits on a busy domain recomputes it. Partitioned attention rejects FlashMLA, beam search, cross attention, helix, custom masks, sparse indices and MRoPE outside the mixed input type. Sparse attention models (DSA, DeepSeek-V4, GLM k-pool) are rejected when LD is on; supporting them is a follow-up. LD weight sharding is #19272.
Test Coverage
kvCacheManagerV2LocalityTest.cpp(real and mock),localizationTest,test_kv_cache_localization{,_wrapper}.py,test_locality_domain_{partitions,dispatch,request}.py,test_cache_transceiver_locality_domains, and, Rubin only,test_locality_domain_attention_gpu.py(LD on matches LD off, GQA and MLA) andtest_stream_localization_e2e.py(Qwen3-4B). All pass on Rubin at the current head; there is no Rubin CI list yet.Performance
One decode attention layer on one GPU with a real KV cache manager and no model weights; no model was run. Heads are per GPU (the model's heads split into shards; source model in brackets). CUDA graph, 106+106 SM split, 4.75 GHz HBM.
Where the gain comes from. With LD off the KV cache is spread over both HBM halves, so each half of the GPU reads half its KV from the other half over the die-to-die link. A quarter of all reads crosses the link in each direction, which caps LD off at 4 x the per-direction link rate. LD on keeps every read in the reading half's own HBM. Raw streaming reads:
Full sweep, batch 1-1024
Every shape at batch 1-1024 x context 1k-32k, 2 repeats each. Cells are LD on / LD off: 🟩 faster in every repeat, 🟥 slower in every repeat, 🟨 tie. Geomean of LD on / LD off:
MLA, 64 heads (128 in 2 shards), fp8 KV (DeepSeek-R1)
GQA, 96 Q / 8 KV heads (1 shard), bf16 KV (GLM-4.7)
GQA, 48 Q / 4 KV heads (96/8 in 2 shards), bf16 KV (GLM-4.7)
GQA, 12 Q / 1 KV head (96/8 in 8 shards), bf16 KV (GLM-4.7)
PR Checklist
Please review the following before submitting your PR:
PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.
PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.
Test cases are provided for new code paths (see test instructions)
If PR introduces API changes, an appropriate PR label is added - either
api-compatibleorapi-breaking. Forapi-breaking, includeBREAKINGin the PR title.Any new dependencies have been scanned for license and vulnerabilities
CODEOWNERS updated if ownership changes
Documentation updated as needed
The reviewers assigned automatically/manually are appropriate for the PR.
Please check this after reviewing the above items as appropriate for this PR.
GitHub Bot Help
To see a list of available CI bot commands, please comment
/bot help.🤖 Generated with Claude Code