Wire the symm current exchange into the CP shared-KV prefetchers
The bs=1 MLA/index prefetchers replaced their consume-side trailing-range NCCL all-reduce with the staging exchange: fill current rows straight into this round's staging span (token-KV collapses to one cached index_copy; the index fill kernel is just pointed at the staging page inverse), cp_symm_barrier, then gather ALL current pages — this rank's own included — from the stagings into the prefetched dense buffer. The symm+prefetcher FAIL_FAST is gone. Rank-uniformity moves with it: staging registration now also happens in maybe_create (batch-logical gates, before any per-rank miss can diverge), because with a prefetcher active the sync compose runs only on per-rank misses and its lazy collective registration would hang. A hit/miss divergence itself stays barrier-safe — both the prefetch consume and the sync-compose fallback execute exactly one begin_round + barrier per (layer, kind), and the counting barrier is shape-free (unlike the AR pair it replaces, which would shape-mismatch). Found by the new index test phase: the fill/remap kernel family skips page id 0 as the SGLang dummy page, so a 0-based first staging slot was never written. The staging layout now reserves row 0 (slot of current page i = i + 1) for every kind, matching the convention instead of depending on per-kernel behavior. Launch-path cost: per-(kind,parity) peer pointer tables and the [pool|staging] concatenations are precomputed/cached (identity pinned by holding the pool-table reference); all prefetch descriptors, staging row indices, and mixed_locs are built once per batch. Validated on g0033 8xH200: 151 unit tests; 8-rank byte-exactness for token sync symm (8 layers), index sync symm (4 layers, new phase), and MLA + index prefetch consume_prefix_with_current vs the legacy sync compose. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -134,11 +134,11 @@ class TestComposePlanSymm(unittest.TestCase):
|
||||
self.assertEqual(plan.current_dense_pages.tolist(), [3, 4, 5])
|
||||
self.assertEqual(plan.symm_all_owner_ranks[:2].tolist(), [0, 1])
|
||||
self.assertEqual(plan.symm_all_owner_ranks[2:].tolist(), [11, 8, 11])
|
||||
self.assertEqual(plan.symm_all_src_pages[2:].tolist(), [0, 1, 2])
|
||||
self.assertEqual(plan.symm_all_src_pages[2:].tolist(), [1, 2, 3])
|
||||
# staging page inverse: current dense pages 3,4,5 -> staging slots
|
||||
# 0,1,2; everything else (dummy, prefix pages) -> -1.
|
||||
# 1,2,3 (slot 0 = dummy row); everything else -> -1.
|
||||
self.assertEqual(
|
||||
plan.staging_page_inverse.tolist(), [[-1, -1, -1, 0, 1, 2]]
|
||||
plan.staging_page_inverse.tolist(), [[-1, -1, -1, 1, 2, 3]]
|
||||
)
|
||||
|
||||
def test_writer_count_mismatch_fails_fast(self):
|
||||
@@ -310,7 +310,9 @@ class TestComposeStaging(unittest.TestCase):
|
||||
region_in_half = {}
|
||||
for kind in CpComposeStaging.KINDS:
|
||||
region_in_half[kind] = half
|
||||
half += staging._align(capacity_pages * staging._page_nbytes[kind])
|
||||
half += staging._align(
|
||||
(capacity_pages + 1) * staging._page_nbytes[kind]
|
||||
)
|
||||
for parity in (0, 1):
|
||||
for kind in CpComposeStaging.KINDS:
|
||||
staging._region_offsets[(kind, parity)] = (
|
||||
@@ -318,6 +320,8 @@ class TestComposeStaging(unittest.TestCase):
|
||||
)
|
||||
staging._slab = torch.zeros(2 * half, dtype=torch.uint8)
|
||||
staging.peer_slab_bases = torch.full((8,), 1 << 20, dtype=torch.int64)
|
||||
for key, offset in staging._region_offsets.items():
|
||||
staging._region_peer_ptrs[key] = staging.peer_slab_bases + offset
|
||||
return staging, half
|
||||
|
||||
def test_regions_are_disjoint_and_parity_halves_do_not_overlap(self):
|
||||
@@ -326,12 +330,12 @@ class TestComposeStaging(unittest.TestCase):
|
||||
idx0 = staging.buffer("index", 0)
|
||||
kv1 = staging.buffer("token_kv", 1)
|
||||
# kv and index regions of one half are adjacent but disjoint
|
||||
# (index starts at the 256-aligned end of kv).
|
||||
self.assertEqual(idx0.data_ptr() - kv0.data_ptr(), staging._align(4 * 1000))
|
||||
# (index starts at the 256-aligned end of kv; +1 = dummy row 0).
|
||||
self.assertEqual(idx0.data_ptr() - kv0.data_ptr(), staging._align(5 * 1000))
|
||||
# The parity-1 half starts exactly one half past parity 0.
|
||||
self.assertEqual(kv1.data_ptr() - kv0.data_ptr(), half)
|
||||
self.assertEqual(kv0.numel(), 4 * 1000)
|
||||
self.assertEqual(idx0.numel(), 4 * 300)
|
||||
self.assertEqual(kv0.numel(), 5 * 1000)
|
||||
self.assertEqual(idx0.numel(), 5 * 300)
|
||||
|
||||
def test_peer_region_ptrs_offset_matches_local_layout(self):
|
||||
staging, half = self._staging()
|
||||
@@ -342,7 +346,7 @@ class TestComposeStaging(unittest.TestCase):
|
||||
self.assertTrue(
|
||||
torch.equal(
|
||||
staging.peer_region_ptrs("index", 1),
|
||||
base + half + staging._align(4 * 1000),
|
||||
base + half + staging._align(5 * 1000),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user