Fix BackendImplementationMissing: repair codegen registration skipped by circular-import partial cache - #3286
Fix BackendImplementationMissing: repair codegen registration skipped by circular-import partial cache#3286mengluy0125 wants to merge 2 commits into
Conversation
Summary: `_find_device` scans kernel arguments for a `torch.device`/`torch.Tensor` to determine the launch device, recursing into nested `tuple`/`list`/`dict` containers. The recursion passed each element directly as `_find_device(item)`. `_find_device` treats its argument as a tuple of args and iterates it (`for arg in args`). Passing a bare `item` that is itself a `torch.Tensor` made the recursive call iterate over the tensor (`for arg in tensor`), materializing the rows of the tensor on the host before the first row matched the `isinstance(arg, torch.Tensor)` check -- a real per-call host overhead on the hot argument-processing path. A 0-dim scalar tensor also fails to iterate at all. Wrap the recursed element in a single-element tuple, `_find_device((item,))`, so the recursion sees exactly one arg and hits the `torch.Tensor` fast path directly. Behavior is unchanged for every previously-working case; the tensor is no longer iterated. Differential Revision: D114780871
… by circular-import partial cache
Summary:
Under torch.compile in packaged runtimes (e.g. aps_ads_vm training), Helion kernels
could fail at pt2_warmup with `BackendImplementationMissing: Backend 'triton' is
missing required implementation: codegen for API function load`.
Root cause: `import_backend_codegen()` registers each backend's per-op codegen by
`importlib.import_module("<pkg>._codegen_modules")`. If that codegen module (or one
of its leaf modules such as `triton/memory_ops.py`) is already present in
`sys.modules` in a partially-initialized state -- a circular import during package
init cached it before its module-scope `_decorators.codegen` / `register_codegen`
handlers ran -- `import_module` returns the incomplete module and the registrations
are silently skipped, leaving `APIFunc._codegen` empty for that backend. This is
import-order dependent (nondeterministic) and surfaces only at codegen time.
Fix:
- `CodegenDict.__missing__`: when a backend codegen key is absent, attempt a
one-time global repair (imports are settled by codegen time) and retry before
raising KeyError.
- `backend_registry.repair_backend_codegen()`: reload each backend's codegen leaf
modules so their registration decorators re-run.
- Make codegen registration idempotent (`_decorators.codegen`,
`aten_lowering.register_codegen`) so the repair reload cannot trip the
"already registered" assert.
Differential Revision: D114782445
|
@mengluy0125 has exported this pull request. If you are a Meta employee, you can view the originating Diff in D114782445. |
ethche
left a comment
There was a problem hiding this comment.
Looks good but marked some areas of concern. Could you take a look?
I asked codex, and it suggested something like this:
- Add a locked ensure_backend_codegen() in backend_registry.
- Set its completion flag only after successful reload.
- Call it before falling back to "common" in CodegenDict.
- Call it when AtenLowering cannot find a backend handler, then retry.
| def __missing__(self, key: str) -> Callable[[CodegenState], object]: | ||
| if key != "common" and "common" in self: | ||
| return self["common"] | ||
| # A backend codegen can be absent because its codegen module was cached |
There was a problem hiding this comment.
Could repair happen before falling back to common? Otherwise a missing backend-specific registration silently uses the common implementation and never triggers repair. Maybe double check
| assert backend not in self.codegen_impls, ( | ||
| f"codegen already registered for backend {backend!r}" | ||
| ) | ||
| # Idempotent: repair_backend_codegen() may reload this codegen module |
There was a problem hiding this comment.
What triggers repair when an Aten codegen registration is missing?
| # this dict empty). By codegen time imports are settled, so force-complete | ||
| # the registrations once and retry before giving up. | ||
| if key != "common" and not CodegenDict._repaired: | ||
| CodegenDict._repaired = True |
There was a problem hiding this comment.
Should _repaired be set only after repair succeeds, with synchronization? As written, an exception or concurrent lookup permanently skips repair.
Summary:
Under torch.compile in packaged runtimes (e.g. aps_ads_vm training), Helion kernels
could fail at pt2_warmup with
BackendImplementationMissing: Backend 'triton' is missing required implementation: codegen for API function load.Root cause:
import_backend_codegen()registers each backend's per-op codegen byimportlib.import_module("<pkg>._codegen_modules"). If that codegen module (or oneof its leaf modules such as
triton/memory_ops.py) is already present insys.modulesin a partially-initialized state -- a circular import during packageinit cached it before its module-scope
_decorators.codegen/register_codegenhandlers ran --
import_modulereturns the incomplete module and the registrationsare silently skipped, leaving
APIFunc._codegenempty for that backend. This isimport-order dependent (nondeterministic) and surfaces only at codegen time.
Fix:
CodegenDict.__missing__: when a backend codegen key is absent, attempt aone-time global repair (imports are settled by codegen time) and retry before
raising KeyError.
backend_registry.repair_backend_codegen(): reload each backend's codegen leafmodules so their registration decorators re-run.
_decorators.codegen,aten_lowering.register_codegen) so the repair reload cannot trip the"already registered" assert.
Differential Revision: D114782445