fix(rng): make default_generators iterable instead of looping forever - #52
Merged
Conversation
The three `default_generators` shims (metax compat, cuda compat, and the flagos module) stand in for a value upstream types as a *tuple*, so callers are entitled to iterate it, slice it or wrap it in `list()`. All three were list-like proxies defining only `__getitem__`/`__len__`, and `__getitem__` was unbounded. With no `__iter__`, Python falls back to the legacy iteration protocol: call `__getitem__(0, 1, 2, ...)` until IndexError. An unbounded `__getitem__` never raises, so `for g in torch.cuda.default_generators` became an infinite loop that allocated a fresh CUDA generator on every step. It presented as a hang rather than an error, which is why nothing surfaced it -- found while walking the generator list on an 8-card C550, where the process sat at 200% CPU indefinitely. The flagos variant did not hang but was equally wrong: out-of-range indices surface as a RuntimeError from C++, which aborts iteration instead of ending it. All three now define `__iter__` and bounds-check `__getitem__`, raising IndexError past the device count and wrapping negative indices the way the tuple they replace does. Slices are supported for the same reason. Regression test is parametrized over both the `torch.cuda` and `flagos` shims and asserts all three properties; verified to hang on the unfixed tree and pass on the fixed one. Verified on 8xC550 (metax boxing): RNG suite 104 passed/2 skipped/1 xfailed (vendor) and 106 passed/1 xfailed (FlagGems). Wider probe of 91 RNG ops covering every overload family, dtype and nn.init entry point: 89/91 clean on the vendor path (the 2 are the known native_dropout schema gap) and 91/91 under FlagGems. Distribution moments checked against theory on all 8 cards. Both ruff gates pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
zhaoyinglia
approved these changes
Aug 4, 2026
AlexMa616
pushed a commit
to BrianPei/PyTorch-Plugin-FL
that referenced
this pull request
Aug 4, 2026
…flagos-ai#52) The three `default_generators` shims (metax compat, cuda compat, and the flagos module) stand in for a value upstream types as a *tuple*, so callers are entitled to iterate it, slice it or wrap it in `list()`. All three were list-like proxies defining only `__getitem__`/`__len__`, and `__getitem__` was unbounded. With no `__iter__`, Python falls back to the legacy iteration protocol: call `__getitem__(0, 1, 2, ...)` until IndexError. An unbounded `__getitem__` never raises, so `for g in torch.cuda.default_generators` became an infinite loop that allocated a fresh CUDA generator on every step. It presented as a hang rather than an error, which is why nothing surfaced it -- found while walking the generator list on an 8-card C550, where the process sat at 200% CPU indefinitely. The flagos variant did not hang but was equally wrong: out-of-range indices surface as a RuntimeError from C++, which aborts iteration instead of ending it. All three now define `__iter__` and bounds-check `__getitem__`, raising IndexError past the device count and wrapping negative indices the way the tuple they replace does. Slices are supported for the same reason. Regression test is parametrized over both the `torch.cuda` and `flagos` shims and asserts all three properties; verified to hang on the unfixed tree and pass on the fixed one. Verified on 8xC550 (metax boxing): RNG suite 104 passed/2 skipped/1 xfailed (vendor) and 106 passed/1 xfailed (FlagGems). Wider probe of 91 RNG ops covering every overload family, dtype and nn.init entry point: 89/91 clean on the vendor path (the 2 are the known native_dropout schema gap) and 91/91 under FlagGems. Distribution moments checked against theory on all 8 cards. Both ruff gates pass. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
zhaoyinglia
pushed a commit
that referenced
this pull request
Aug 4, 2026
* ci: add configured MetaX build and integration workflows * ci: fix MetaX workflow input expressions * ci: persist isolated MetaX Python path * ci: preserve platform environment in integration tests * ci: isolate CUDA wheel from vendor torch * ci: install CUDA integration test dependencies * ci: tolerate CUDA images without standalone libc10_cuda * ci: pin CUDA CI image digest * ci: build MetaX integration wheel locally * ci: diagnose CUDA library layout * ci: fix native library staging * ci: load isolated native runtimes * ci: build CUDA integration wheel locally * ci: stage CUDA runtime dependencies * ci: diagnose MetaX runtime symbols * ci: retain MetaX CUDA runtime dependency * fix: provide MetaX generator ABI shim * fix: use vendor CUDA generator for MetaX boxing * ci: capture first MetaX operator failure * fix(rng): close the last two generator-injection gaps and put RNG under CI (#49) The unified-RNG contract -- one `torch.manual_seed` reaching every RNG op on the flagos device -- had no CI protection at all, and two codegen templates still leaked. Injection gaps. Generator-less RNG overloads carry no `Generator?` in their schema, so `_generator_inject_line` never fires and they fall back to ATen's own default CUDA generator, unreachable from `torch.manual_seed` on a CPU-torch wheel. `torch.randint_like(...)` and every RNG out-variant dispatch to exactly such overloads. The fix has to be made once per template because the ATen insertion point differs each time: `*_like` takes the generator before `dtype` (it carries `self`, so it lands on gen_functional_pure rather than gen_factory), while out-variants take it before the first of names/memory_format/out. That second one covered 11 kernels -- rand/randn `.out` and `.names_out`, rand_like/randn_like `.out`, randint `.out`/`.low_out`, randint_like `.out`/`.low_dtype_out`, randperm `.out`. The three base sets are now module-level constants, one per template, so all three positions read together. `_is_in_bad_fork`. `torch.random._seed_custom_device` needs both it and `manual_seed_all` to seed a custom device; only the latter existed, so every `torch.manual_seed()` warned "Set seed for `flagos` device does not take effect" -- false, since seeding really goes through the patched `torch.cuda.manual_seed_all`. Adding it silences the warning and makes `flagos.initial_seed()` track the global seed. Tests. The old file was marked `flaggems_python` with no `main_ops`, while CI selects only `-m main_ops` and `-m "flaggems and main_ops"` -- so it never ran. It also skipped itself entirely without FLAGOS_USE_FLAGGEMS, on the premise that the native path could not be reproducible; C++ generator injection made that premise obsolete. Rewritten to run under both configs (the native-path injection is only exercised by the vendor run) and to cover 39 ops grouped by the mechanism each stresses, plus the seed plumbing itself, multi-device generators, and distribution correctness. CI now collects 105 RNG tests in the vendor job and 2 in the FlagGems job, up from 0; no workflow change was needed, the markers are the wiring. Two gaps injection cannot reach are recorded as non-strict xfails rather than left implicit: `native_dropout` has no `Generator?` anywhere in its ATen schema, so the vendor path has no argument to inject into; and FlagGems `multinomial` launches its Triton kernel against the wrong device for any index != 0, a device-context bug on that path rather than an RNG one. Verified on 8xA100 under both configs: 102 passed/2 skipped/1 xfailed (vendor) and 104 passed/1 xfailed (FlagGems). Regression clean -- main_ops 115 passed, flaggems main_ops 14 passed, flaggems_python 27 passed, native 445 passed/3 xpassed (pre-existing conv1d segfault excluded). Both ruff gates pass. * fix(rng): make default_generators iterable instead of looping forever (#52) The three `default_generators` shims (metax compat, cuda compat, and the flagos module) stand in for a value upstream types as a *tuple*, so callers are entitled to iterate it, slice it or wrap it in `list()`. All three were list-like proxies defining only `__getitem__`/`__len__`, and `__getitem__` was unbounded. With no `__iter__`, Python falls back to the legacy iteration protocol: call `__getitem__(0, 1, 2, ...)` until IndexError. An unbounded `__getitem__` never raises, so `for g in torch.cuda.default_generators` became an infinite loop that allocated a fresh CUDA generator on every step. It presented as a hang rather than an error, which is why nothing surfaced it -- found while walking the generator list on an 8-card C550, where the process sat at 200% CPU indefinitely. The flagos variant did not hang but was equally wrong: out-of-range indices surface as a RuntimeError from C++, which aborts iteration instead of ending it. All three now define `__iter__` and bounds-check `__getitem__`, raising IndexError past the device count and wrapping negative indices the way the tuple they replace does. Slices are supported for the same reason. Regression test is parametrized over both the `torch.cuda` and `flagos` shims and asserts all three properties; verified to hang on the unfixed tree and pass on the fixed one. Verified on 8xC550 (metax boxing): RNG suite 104 passed/2 skipped/1 xfailed (vendor) and 106 passed/1 xfailed (FlagGems). Wider probe of 91 RNG ops covering every overload family, dtype and nn.init entry point: 89/91 clean on the vendor path (the 2 are the known native_dropout schema gap) and 91/91 under FlagGems. Distribution moments checked against theory on all 8 cards. Both ruff gates pass. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: nate.river <lvyufeng@cqu.edu.cn> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The three
default_generatorsshims —torch_fl/accelerator/metax/_metax_compat.py,torch_fl/accelerator/cuda/_cuda_compat.py, andtorch_fl/flagos/__init__.py— stand in for a value upstream types as a tuple (torch/cuda/__init__.py:158), so callers are entitled to iterate it, slice it, or wrap it inlist(). All three were list-like proxies defining only__getitem__/__len__, and__getitem__was unbounded.With no
__iter__, Python falls back to the legacy iteration protocol: call__getitem__(0, 1, 2, ...)untilIndexError. An unbounded__getitem__never raises, so:became an infinite loop that allocated a fresh CUDA generator on every step. It presented as a hang rather than an error, which is why nothing surfaced it — found while walking the generator list on an 8-card C550, where the process sat at 200% CPU indefinitely (34 min before I killed it). The
flagosvariant did not hang but was equally wrong: out-of-range indices surface as aRuntimeErrorfrom C++, which aborts iteration instead of ending it.All three now define
__iter__and bounds-check__getitem__, raisingIndexErrorpast the device count and wrapping negative indices the way the tuple they replace does. Slices are supported for the same reason.Testing
New
test_default_generators_iterableis parametrized over both thetorch.cudaandflagosshims and asserts all three properties (iteration terminates atdevice_count, slicing works, out-of-range raisesIndexError). Verified to hang on the unfixed tree (stashed the fix, pytest timed out) and pass on the fixed one — not merely green-on-green.Verified on 8xC550 (MetaX boxing,
torch 2.10.0+metax3.8.1.0,triton-metax 3.6.0):FLAGOS_METAX_BOXING=1)+FLAGOS_USE_FLAGGEMS=1)test_rng_dispatch.py-m main_opsThis branch also confirms the unified-RNG work from #39/#49 is complete on MetaX — the boxing build compiles 103
GetFlagosDefaultCudaGeneratorinjection sites, andpython_op_caller.ccrides in viaFLAGGEMS_PYTHON=ON. Beyond the repo suite I ran an independent probe of 91 RNG ops covering every overload family (.out/.names_out/.generator/*_like), fp16/bf16/fp64/int32, and thenn.init.*entry points, checking run + same-seed-same-draw + different-seed-differs for each: 89/91 vendor, 91/91 FlagGems. The 2 vendor misses are exactly the documentednative_dropoutschema gap (already an xfail).Distribution moments were checked against theory rather than just reproducibility, since a mis-bound overload can be perfectly reproducible and still wrong: randn skew +0.004 / excess kurtosis −0.0005, poisson(4) mean 4.0001 var 3.9925, randint chi² 6.99 (df=9), gamma(3) mean 2.997 var 2.992, dirichlet rows sum to 1, cauchy median +0.001, consecutive draws uncorrelated (r = −0.009),
get_state/set_stateround-trip exact. All 8 cards pass independently, and seeding card 0 does not perturb card 1.ruff==0.15.12:ruff check .andruff format --check .both pass.Not caused by this change
Under
-m "flaggems and main_ops"on MetaX,test_mm_dispatch/test_mean_dispatchtest_dispatch_log_flaggems_runtimefail: they assert-> flagos_python, butbackends_metax_flaggems.confdeliberately routesmm/bmm/mean.dimtocuda. Confirmed reproducing on the unmodified tree.🤖 Generated with Claude Code