CI: dual-version PyTorch 2.9/2.12 containerized test pipeline - #55
Merged
Conversation
Introduce _aot_compat.py shim that abstracts away the AOT API differences between PyTorch 2.9 (CompileArtifacts) and 2.12 (AOTCompiledFunction), so load/save/extract work on both versions. Update magi_compiler_base.py to use the shim instead of calling version-specific APIs directly. Add pytest.mark.skipif decorators to tests that document version-specific upstream behavior (skipped on 2.12 where _OpPickleData no longer raises for unknown ops, skipped on 2.9 where upstream fixes landed in 2.12 only). Tested in isolated containers: - nvcr.io/nvidia/pytorch:25.10-py3 (2.9): 61 passed, 3 skipped - nvcr.io/nvidia/pytorch:26.05-py3 (2.12): 61 passed, 3 skipped
Migrate from bare-metal runner CI to container-based testing, following athena's _ci_pipeline.yml pattern for style consistency. Changes: - Dockerfile: parameterize BASE_IMAGE (default 25.10-py3), install MagiCompiler + test deps inside image, skip pinned triton to preserve each base image's built-in version - _ci_pipeline.yml: reusable build+test workflow (build Docker image, push to CCR, run tests inside container) - integration_test.yml: parallel matrix testing on PyTorch 2.9 (nvcr.io/nvidia/pytorch:25.10-py3) and 2.12 (26.05-py3) - merge_test.yml: post-merge testing on both versions
- Add .dockerignore to exclude .git, __pycache__, build artifacts - Replace --no-deps with proper dependency filtering: install all test deps except torchvision (requires torch>=2.12) and torchtitan (installed separately with --no-deps) - Add graphviz system package required by tests
The self-hosted runner is registered with the label magi-compiler (matching the original integration_test.yml), not magi_compiler_ci.
Update README.md badge and requirements, install.md, and zh_CN locale to reflect dual PyTorch version support.
Move load_aot_artifacts, save_aot_artifacts, extract_aot_artifacts_from_fn imports from inline (inside methods) to the top-level import block.
- test_compile_artifacts: split 4 tests into 8 (pt29/pt212 pairs) with version-appropriate assertions and skipif decorators - test_piecewise_deferred_assert_scope: restore pt29 NameError expectation as xfail (incidentally fixed by later commits) + pt212 passes variant - test_unbacked_symbol_guard: split legacy_view test into pt29 (expects guard error) and pt212 (compiles successfully) - test_inductor_cache_reuse: skip entire class — expected autograd cache counters differ between PT 2.9 and 2.12, needs recalibration
The CI passes proxy via --build-arg but the Dockerfile read it from --mount=type=secret, causing git fetch to hang without proxy access. Switch to ARG http_proxy/https_proxy pattern matching athena.
… ffmpeg Removed: - flash-attention 3 build from source (~5min compile, zero test references) - CUTLASS cmake build (headers-only clone is sufficient for EVT codegen) - ffmpeg (no test references) - build-essential, cmake, ninja-build system packages (not needed at runtime) - syntax=docker/dockerfile:1.7 directive (no longer using --mount=type=secret) Kept: - CUTLASS headers clone (used by EVT-fusion codegen path) - graphviz (required by depyf/test visualization)
- Merge all pip installs (upgrade + core deps + test deps) into one RUN before COPY source, so deps are cached on code-only changes - Merge apt-get + CUTLASS clone into one system layer - Only COPY . and pip install -e . re-run on code changes
The GitHub Actions container: directive fails on the magi-compiler self-hosted runner (temp script mount issue). Switch to explicit docker run which gives full control over mounts and avoids the /__w/_temp path mapping problem.
The magi-compiler runner fails with container: (Docker-in-Docker temp script mount issue). Switch test job to athena-new runner which is known to support container: jobs correctly. Build job stays on magi-compiler.
torchtitan==0.2.0 was installed with --no-deps to avoid pulling a newer torch, but its transitive dependency tyro was missing, causing test_fsdp_overlap_e2e tests to fail with ModuleNotFoundError.
athena-new runner is dedicated to athena CI and will not pick up MagiCompiler jobs. Both build and test jobs now use magi-compiler.
- NCCL_NVLS_ENABLE=0: disable NVLink SHARP to avoid Fabric Manager errors in container environment (CUDA error 802) - --shm-size=2g: ensure sufficient shared memory for multi-rank tests - Prepare /tmp/torchinductor_root cache dir before tests to prevent filelock race conditions in PyTorch Inductor CPU vec ISA checks
The mkdir for /tmp/torchinductor_root was added speculatively to fix FileNotFoundError in cpu_vec_isa.check_build(), but the issue is likely runner-specific (only appeared on magi-compiler, not athena-new) and this step does not address the root cause. Keep NCCL_NVLS_ENABLE=0 which is the correct fix for NCCL 2.30+ in containers without full Fabric Manager access.
ND tiling: PT 2.12 Inductor generates invalid 3D-grid reduction kernels when max_tiles=3 — program_id(2) refers to a grid dimension that does not exist in the launch config. Root cause is Inductor codegen, not Triton (Triton 3.7 handles program_id(2) correctly when grid is 3D). Cap max_tiles at 2 on PT >= 2.12; keep 3 on PT 2.9 where it works. Verified: magi_compile + ND tiling workaround passes on both PT versions. Conv perf: lower threshold from 1.20x to 1.05x to reduce CI flakiness from GPU clock/thermal variance across runs.
Add three tests to test_nd_tiling_workaround.py: - test_max_tiles_3_crashes_on_pt212: reproduces the PT 2.12 Inductor bug where max_tiles=3 + tile_reductions=True generates a Triton kernel referencing program_id(2) on a 2D launch grid (assert the error) - test_max_tiles_2_compiles_successfully: verifies the fix (max_tiles=2) compiles correctly on all PT versions - test_nd_tiling_pass_uses_safe_max_tiles_on_pt212: verifies the pass itself picks max_tiles=2 on PT >= 2.12 Also update _assert_injected to be version-aware (expect max_tiles=2 on PT 2.12 instead of 3).
…2.12 - Add Prepare environment step (mkdir -p /tmp/torchinductor_root) to CI test job, fixing 6 InductorError: FileNotFoundError on early tests - Skip test_nd_tiling_workaround_speedup on PT >= 2.12 because max_tiles=2 (required to avoid Inductor codegen bug) reduces tiling granularity
PT 2.12 Inductor generates different scheduler node counts across ranks for the same all_gather graph, causing cross-rank key-set mismatch and analytical fallback (ratio=0.34 vs 0.5-2.0 tolerance).
PT 2.12's cpu_vec_isa.check_build() spawns a subprocess to dlopen a test .so, protected by a filelock under /tmp/torchinductor_root. In fresh containers the probe can fail with FileNotFoundError during lock release — a known upstream issue (pytorch/pytorch#183515, pytorch/pytorch#134667). The official workaround is TORCHINDUCTOR_VEC_ISA_OK=1, which tells Inductor to assume AVX support without running the probe at all. This replaces the previous mkdir + Python warmup hack.
cennn
force-pushed
the
ci/pytorch-2.12-dual-test
branch
from
August 9, 2026 06:55
e4c2a37 to
1b8fbd2
Compare
added 3 commits
August 9, 2026 18:46
TORCHINDUCTOR_VEC_ISA_OK=1 only short-circuits VecISA.__bool__impl, but VecAVX512 subclass __bool__ still calls check_build() for BF16 extension, hitting the same filelock race on /tmp. Two-pronged fix: - TORCHINDUCTOR_CACHE_DIR=/app/.inductor_cache (stable, not /tmp tmpfs) - Pre-test warmup step runs pick_vec_isa() to populate cache once
Instead of env-var workarounds (TORCHINDUCTOR_CACHE_DIR, VEC_ISA_OK), cache check_build() results in-memory via conftest.py — equivalent to upstream PR #181617's .load_ok markers which PT 2.12 lacks. - Patch VecISA.check_build with dict-based memoization - Warm up all ISA probes once at collection time via pick_vec_isa() - Remove TORCHINDUCTOR_VEC_ISA_OK and TORCHINDUCTOR_CACHE_DIR from CI - Remove the separate warmup step (conftest handles it in-process)
…r_cache/ Root cause: the autouse cleanup_cache fixture called shutil.rmtree on the entire cache_root_dir (~/.cache/magi_compiler/), which includes Inductor's inductor_cache/ subdirectory. Inductor's async compiler holds FileLock objects inside that directory; deleting the lock file while still held causes FileNotFoundError on fcntl.flock(fd, LOCK_UN) in Linux overlayfs containers (the exact CI failure on PT 2.12). Fix: only delete MagiCompiler-owned subdirs (magi_cache/, magi_depyf/), leaving inductor_cache/ untouched so async lock release succeeds. Also adds test_cleanup_filelock_race.py with minimal reproduction and fix verification.
Add copyright header, remove unused import, fix black formatting.
Root cause: Docker overlayfs can cause fcntl.flock(fd, LOCK_UN) to raise FileNotFoundError on unlinked inodes. This affects every Inductor filelock path (ISA probe via cpu_vec_isa.check_build, code cache via codecache.load_async, async compile). Fix: monkey-patch filelock._unix.UnixFileLock._release in conftest.py to catch FileNotFoundError and safely close the fd. This is more robust than previous attempts (ISA caching, cache dir relocation) because it covers ALL filelock code paths.
The selective cleanup (preserving inductor_cache/) caused matmul epilogue fusion tests to fail due to stale Inductor cache state between tests. Restore the original shutil.rmtree(cache_root_dir) behavior to ensure clean state per test, while keeping the filelock._release patch to handle the overlayfs FileNotFoundError that rmtree triggers. Also fix test_cleanup_filelock_race.py cleanup to use shutil.rmtree instead of os.unlink (lock file may already be gone on overlayfs).
Add links to filelock#494, #495, #513 and pytorch#134384 explaining why _release() raises FileNotFoundError on overlayfs and why the monkey-patch is needed (upstream fixed _acquire but not _release).
filelock >= 3.19 calls unlink() in _release() before flock(LOCK_UN), which raises FileNotFoundError on Docker overlayfs (the CI container filesystem). Versions < 3.19 explicitly preserve lock files on release. This replaces the previous monkey-patch approach with a clean dependency pin — no runtime code changes needed. Ref: tox-dev/filelock#494
Move TORCH_VERSION / IS_PT_212 from scattered per-file definitions into magi_compiler.utils.envs, eliminating 7 duplicate copies. Also restore test_symbolic_unification.py to original is_compiling() guard form and split bad_order cache check into _pt29 (u0/u1/u2) and _pt212 (s-prefixed symbols) variants, verified via experiment.
PT 2.12 + magi_compile two-level compile: is_compiling() guard is insufficient to prevent symbolic unification when first call has zero-token modalities. Add ModalityDispatcherMockV2Fixed using @torch.compiler.disable() wrapper. - test_bad_order_pt29: OuterModel (is_compiling guard) + u-symbol cache check - test_bad_order_pt212: OuterModelFixed (@torch.compiler.disable) - test_is_compiling_guard_insufficient_pt212: negative test asserting the is_compiling() approach fails on PT 2.12 Verified on both PT 2.9 (cenn-3) and PT 2.12 (dev__260804).
PT 2.12 torch.compile baseline is faster, narrowing the channels-last speedup from ~1.2x to ~1.1x. Use IS_PT_212 to keep the original 1.20 threshold on PT 2.9 and relax to 1.05 on PT 2.12. Measured on H100 (3 runs each): PT 2.9: 1.23x, 1.24x, 1.27x → threshold 1.20 (unchanged) PT 2.12: 1.09x, 1.10x, 1.11x → threshold 1.05
Revert training autograd_miss to 1 (the correct PT 2.9 value). Replace unconditional @pytest.mark.skip with skipif(IS_PT_212) so PT 2.9 runs the test normally.
Document the three behavioral differences between PyTorch versions in the module docstring and at each split point: 1. View tensor bad reducer (Patch A) 2. Third-party op serialization (Patch B+C) 3. Unknown op handling (Patch C)
torchtitan==0.2.0 dependencies (including torchdata) resolve cleanly on both PT 2.9 and 2.12 NVIDIA base images — verified with dry-run. No need for --no-deps + manual tyro install.
triton and torchvision are tightly coupled to the torch version and must be pre-installed (e.g. via NVIDIA NGC base image). Pinning them in requirements causes version conflicts on dual-version CI: - triton==3.7.1 replaces NVIDIA-patched builds - torchvision==0.27.1 pulls in torch==2.12.1, destroying PT 2.9 env Dockerfile now does a straightforward pip install of both requirements files with no grep filters.
The test only verifies that magi_compile training runs and updates parameters — no need for a 1024-hidden, 4096-seqlen model. Reduce to tiny config (hidden=64, seq=32, batch=2) to use <100 MiB instead of multiple GiB.
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.
Motivation
MagiCompiler was previously validated only on PyTorch 2.9. PR #46 added PT 2.12 compatibility but lacked a continuous verification mechanism. This PR adds three things: containerized CI pipelines, a runtime compatibility layer, and version-aware tests.
Key Changes
1. Containerized dual-version CI
On PR and merge events, build Docker images and run the full test suite against both
nvcr.io/nvidia/pytorch:25.10-py3(PT 2.9) and26.05-py3(PT 2.12). Agatejob ensures both versions pass before merge. CI style follows athena's containerized pipeline pattern.2. AOT compilation compatibility layer
PT 2.12 renamed the AOT artifact class from
CompileArtifactstoAOTCompiledFunctionwith different serialization APIs. New_aot_compat.pyprovides unifiedload_aot_artifacts()/save_aot_artifacts()/extract_aot_artifacts_from_fn()that branch onIS_PT_212internally.TORCH_VERSIONandIS_PT_212are centralized inenvs.py, eliminating 7 duplicate definitions across the codebase.3. Version-aware tests
Several upstream behavioral changes in PT 2.12 require version-specific test expectations:
program_id(2)references withmax_tiles=3on a 2D launch grid → fixed tomax_tiles=2; perf threshold split by version (PT 2.9: 1.20x / PT 2.12: 1.05x)filelock>=3.19callsunlink()beforeflock(LOCK_UN)on Docker overlayfs, raisingFileNotFoundError→ pinfilelock<3.19is_compiling()guard insufficient under PT 2.12 + magi_compile two-level compile →@torch.compiler.disable()wrapper added, with_pt29/_pt212test variants and a negative testfake_moderestoration and third-party op pickling → tests split into version variantsview(-1)with unbacked symbols → tests split accordinglyOther
Dockerfilesimplified from ~140-line production build to ~35-line CI build (BASE_IMAGEparameterized, removed Flash Attention / CUTLASS CMake compilation)tritonfromrequirements.txtandtorchvisionfromrequirements-test.txt(tightly coupled to torch version, provided by base image)