Skip to content

Commit 61b883b

Browse files
committed
Bound host memory during tensor-parallel kernel preparation
Compare preparation metadata by broadcasting one reference and reducing agreement flags, avoiding a full copy of every rank's catalog on each rank. Pin FlashInfer to the collective candidate-plan lifetime fix, which releases transient declarations while preserving tuning candidates and allocation accounting. Validation: 28 collective preparation tests passed, including real Gloo agreement and subgroup checks. FlashInfer's 157 focused CPU tests passed. The full Kimi K3.1 TP16 export is still being validated. Design-Signed-Off-By: Codex (GPT-6); design-review=4d3a
1 parent af0907d commit 61b883b

4 files changed

Lines changed: 78 additions & 8 deletions

File tree

‎ignition/preparation/collectives.py‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from datetime import timedelta
99
from pathlib import Path
1010

11+
import torch
1112
import torch.distributed as dist
1213

1314

@@ -184,9 +185,11 @@ def spans_hosts(self):
184185
return len(set(self.hosts)) > 1
185186

186187
def same_on_every_rank(self, value):
187-
values = [None] * len(self.ranks)
188-
dist.all_gather_object(values, value, group=self.group)
189-
return all(item == values[0] for item in values)
188+
reference = [value if dist.get_rank() == self.ranks[0] else None]
189+
dist.broadcast_object_list(reference, src=self.ranks[0], group=self.group)
190+
matching = torch.tensor(int(value == reference[0]), dtype=torch.int32, device="cpu")
191+
dist.all_reduce(matching, op=dist.ReduceOp.MIN, group=self.group)
192+
return bool(matching.item())
190193

191194
def share_selections(self, cache):
192195
"""Adopt rank zero's cached kernel selections on every rank.

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ prepare = [
2222
"torch==2.14.1+cu132",
2323
"torchvision==0.29.1+cu132",
2424
"transformers[torch,vision,chat_template] @ git+https://github.com/huggingface/transformers.git@1d9654e78a23fb167ef539cbd699dfbd3e4830cf",
25-
"flashinfer-python[b12x] @ git+https://github.com/local-inference-lab/flashinfer.git@2e4f4c2c7d34e9836ef08e3dd326eabbad438276",
25+
"flashinfer-python[b12x] @ git+https://github.com/local-inference-lab/flashinfer.git@15fc5a323cdc446c3a1f9ed5de48374d77fc2dcf",
2626
"cuda-python==13.3.1",
2727
"cuda-tile[tileiras]==1.6.0",
2828
"safetensors==0.8.0",

‎tests/preparation/test_collectives.py‎

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
"""Collective preparation cannot launch ahead of a peer or mismatched declaration."""
66

7+
import multiprocessing
78
from types import SimpleNamespace
89

910
import pytest
@@ -290,12 +291,78 @@ def test_sharded_finalists_preserve_empty_shards_and_rank_order():
290291

291292
def test_ranks_report_whether_a_value_matches_every_rank(monkeypatch):
292293
owner = coordinator(monkeypatch)
294+
monkeypatch.setattr(dist, "get_rank", lambda: 4)
295+
296+
def broadcast(values, *, src, group):
297+
assert src == 4 and group is owner.group
298+
assert len(values) == 1
299+
300+
def reduce(matching, *, op, group):
301+
import torch
302+
303+
assert group is owner.group and op == dist.ReduceOp.MIN
304+
assert matching.device.type == "cpu" and matching.dtype == torch.int32
305+
306+
monkeypatch.setattr(dist, "broadcast_object_list", broadcast)
307+
monkeypatch.setattr(dist, "all_reduce", reduce)
293308
assert owner.same_on_every_rank("NVIDIA GB10")
294-
monkeypatch.setattr(dist, "all_gather_object",
295-
lambda states, value, **kwargs: states.__setitem__(slice(None), [value, "NVIDIA GB300"]))
309+
monkeypatch.setattr(dist, "all_reduce", lambda matching, **kwargs: matching.fill_(0))
296310
assert not owner.same_on_every_rank("NVIDIA GB10")
297311

298312

313+
def _equality_rank(rank, world, store, outcome):
314+
from datetime import timedelta
315+
import traceback
316+
317+
import torch
318+
319+
try:
320+
dist.init_process_group("gloo", init_method=f"file://{store}", rank=rank,
321+
world_size=world, timeout=timedelta(seconds=15))
322+
owner = CollectivePreparation(dist.group.WORLD)
323+
catalog = ("NVIDIA GB202", (("projection", {"shape": (64, 128), "dtype": "bf16"}, ()),))
324+
changed = ("NVIDIA GB202", (("projection", {"shape": (65, 128), "dtype": "bf16"}, ()),))
325+
results = (owner.same_on_every_rank(catalog),
326+
owner.same_on_every_rank(changed if rank == world - 1 else catalog),
327+
owner.same_on_every_rank(False))
328+
group = dist.new_group(ranks=[1, 2], backend="gloo", timeout=timedelta(seconds=15))
329+
subgroup = None
330+
if rank in (1, 2):
331+
owner = CollectivePreparation(group)
332+
subgroup = (owner.same_on_every_rank(catalog),
333+
owner.same_on_every_rank(changed if rank == 2 else catalog))
334+
assert not torch.cuda.is_initialized()
335+
outcome.put((rank, (*results, subgroup)))
336+
except BaseException:
337+
outcome.put((rank, traceback.format_exc()))
338+
finally:
339+
if dist.is_initialized():
340+
dist.destroy_process_group()
341+
342+
343+
def test_gloo_value_agreement_reaches_every_rank_and_subgroup(tmp_path):
344+
context = multiprocessing.get_context("spawn")
345+
world = 3
346+
outcome = context.Queue()
347+
processes = [context.Process(target=_equality_rank,
348+
args=(rank, world, tmp_path / "store", outcome)) for rank in range(world)]
349+
for process in processes:
350+
process.start()
351+
try:
352+
results = dict(outcome.get(timeout=30) for _ in processes)
353+
for process in processes:
354+
process.join(timeout=30)
355+
assert process.exitcode == 0
356+
assert results == {0: (True, False, True, None),
357+
1: (True, False, True, (True, False)),
358+
2: (True, False, True, (True, False))}
359+
finally:
360+
for process in processes:
361+
if process.is_alive():
362+
process.terminate()
363+
process.join(timeout=5)
364+
365+
299366
@pytest.mark.parametrize("ring", [False, True])
300367
def test_roce_setup_preserves_routing_and_package_transport_owner(monkeypatch, ring):
301368
import sys

‎uv.lock‎

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)