refactor[next]: add stage observability and Toolchain.translate - #2742
Open
egparedes wants to merge 1 commit into
Open
refactor[next]: add stage observability and Toolchain.translate#2742egparedes wants to merge 1 commit into
egparedes wants to merge 1 commit into
Conversation
2 tasks
egparedes
marked this pull request as ready for review
July 30, 2026 17:58
egparedes
force-pushed
the
otf-split-3-observability
branch
from
July 30, 2026 17:58
e3d68d2 to
1c59da0
Compare
Give the toolchain a sanctioned way to observe and partially run its pipelines, and remove the one consumer that had to reach into them: - `otf.workflow.stage_hook(name, artifact)`: an event hook emitted after each step by the two named pipelines (`backend.Transforms` and `recipes.OTFCompileWorkflow`, via the generic `MultiWorkflow` / `NamedStepSequence` loops) and by `Toolchain.translate`. Artifacts are passed opaquely and never formatted at the emit site; with nothing subscribed the hook body is empty, so emission is an empty callback loop (~160 ns). - `GT4PY_DUMP_STAGES=<dir>` (`config.DUMP_STAGES`) plus the `instrumentation.stage_dump` subscriber, which writes every stage artifact to `<dir>/<program>/<NNN>_<step>.<ext>`. Registered at import time when the variable is set, so a plain program run dumps its stages without any user code; `enable()` / `disable()` cover programmatic use. Exclusive-create with an index bump keeps concurrent compilation workers and same-named programs from overwriting each other. - `Toolchain.translate(definition, compile_time_args)`: the sanctioned partial run (frontend + translation only). It narrows to the standard `OTFCompileWorkflow` shape and raises a clear error on a monolithic backend. Per-call step options are deliberately not offered; a caller needing a variant step builds a variant pipeline with `dataclasses.replace`. - The dace `__sdfg__` reach-in is gone. It duck-typed through `backend.backend.translation`, unwrapped the `CachedStep` by hand, and used `object.__setattr__` to write into the *in-memory-cached* `past_to_itir` stage and its frozen `CompileTimeArgs`. It is replaced by a dace-owned translate-only toolchain variant built with `dataclasses.replace`, plus `Toolchain.translate`. That last change also fixes a real bug: because the mutated stage was the cached one, a second `__sdfg__` call re-transformed an already-transformed program, and any other consumer of that cache entry saw args whose `offset_provider` had been replaced by offset-provider *types*, losing the runtime connectivity tables. A regression test covers it. Removing the reach-in drops the last non-doc consumer of the workflow mixins' `.replace`, which unblocks the pipeline simplification. `GT4PY_DUMP_STAGES` is new opt-in functionality; nothing else changes behavior, and no fingerprinted dataclass gains a field, so no cache keys rotate. Claude-Session: https://claude.ai/code/session_01R8zRtFMhdJ8c96XJYCXkRk
egparedes
force-pushed
the
otf-split-3-observability
branch
from
July 31, 2026 16:15
1c59da0 to
292deb1
Compare
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.
Description
Fourth PR of the
otf-toolchain-splitstack (on top of #2741). This one gives the toolchain a sanctioned way to observe and partially run its pipelines, and then removes the one consumer that previously had to reach into them. It implements the "Stage observability" decision recorded in ADR 0027 (added in #2741).What's new
otf.workflow.stage_hook(name, artifact)— an event hook on the existinginstrumentation.hook_machinery, emitted after each step by the two named pipelines (backend.Transformsandrecipes.OTFCompileWorkflow, through the genericMultiWorkflow/NamedStepSequenceloops) and byToolchain.translate. Artifacts are passed opaquely; nothing is formatted or stringified at the emit site. With no subscriber the hook body is empty, socallbacksis()and an emit costs ~160 ns (about 1 µs per seven-step program compile).GT4PY_DUMP_STAGES=<dir>(config.DUMP_STAGES) plus theinstrumentation.stage_dumpsubscriber, writing each stage artifact to<dir>/<program>/<NNN>_<step>.<ext>. It registers at import time when the variable is set, so a plaingtx.programrun dumps its stages with no user code;enable()/disable()cover programmatic and test use. Files are created exclusively with an index bump on collision, so concurrent compilation workers and same-named programs never overwrite each other. Serialization reuses what the repo already has (the iterator pretty printer,foast_pretty_printer,inspect.getsource, the real generatedsource_code) and falls back torepr.Toolchain.translate(definition, compile_time_args)— the sanctioned partial run: frontend plus the translation step, stopping before bindings and compilation. It narrows to the standardOTFCompileWorkflowshape and raisesNotImplementedErrornaming the toolchain and the offending backend type otherwise. Per-call step options are deliberately not offered — reconfiguration stays composition-time viadataclasses.replace, per ADR 0011's own rule.The dace
__sdfg__reach-in is gone — and it was hiding a bugThe old code (
runners/dace/program.py) duck-typed throughbackend.backend.translation, unwrapped theCachedStepby hand, called the mixin.replace(...), and usedobject.__setattr__to write into the stage returned byTransforms.past_to_itirand its frozenCompileTimeArgs. Sincepast_to_itiris an in-memory cached step, that stage was the cache entry — and the__sdfg__-shaped input and thecompile()-shaped input produce the same cache key (verified: botha54d493f8ee25792).So calling
__sdfg__and thencompile(offset_provider=<mesh>)on the same program hands the compile path a cached stage whoseargs.offset_providerhas been overwritten with offset-provider types, stripped of the runtime connectivity tables. That fails hard atiterator/ir_utils/domain_utils.py:231(assert common.is_neighbor_table(connectivity)) — and underpython -O, which gt4py actively recommends, the assert disappears and you get silently wrong domain inference instead.It's replaced by a dace-owned translate-only toolchain variant built with
dataclasses.replaceplusToolchain.translate, which leaves the cache pristine. This is the one intentional behavior change in the PR, it is a bug fix, andtest_sdfg_conversion_does_not_mutate_gtir_cacheguards it (verified to fail against the old code). The pre-existingtest_halo_exchange_helper_attrsandtest_orchestration.pypass unmodified — that's the equivalence check for everything else.Removing the reach-in also drops the last non-doc consumer of the workflow mixins'
.replace, which is what unblocks the pipeline simplification in the next PR.Notes for review
ProgramSource.source_codeis not always astr. dace stores SDFG JSON as adict, despite the annotation. The dump subscriber therefore serializes non-strsource as pretty JSON. Worth knowing independently of this PR: under the defaultBUILD_JOBS_MODE=PROCESSan exception in the compile pipeline surfaces only atwait_for_compilation(), so this kind of mismatch fails silently and late. Tightening that annotation belongs in its own change.stage_dumpwarns rather than raises on any failure. An observational debug subscriber must never turn a working compile into a failing one — especially when the failure would surface out of context from a worker process. Push back if you disagree.func_to_past,past_lint,field_view_prog_args_transform,past_to_itir,translation,bindings,compilation). The next PR freezes them in explicit__call__methods.BUILD_JOBS_MODE=PROCESSthe compile pipeline runs in a spawned worker. TheGT4PY_DUMP_STAGESenv-var route works there (the worker inherits the environment and registers at import). Settingconfig.DUMP_STAGESprogrammatically and callingstage_dump.enable()does not reach workers, so it dumps frontend stages only —enable()now warns about this. Making workers register too is a follow-up, not something I wanted to widen this PR with.OTFCompileWorkflow, so they dump frontend stages only andtranslate()raises. That's per ADR 0027.Toolchain.translate, which keeps the dace side from needing to know the pipeline's internals at all.Requirements
translate, and the dace cache-mutation regression.)https://claude.ai/code/session_01R8zRtFMhdJ8c96XJYCXkRk