Add multi-op batch screening layer; fix cudagraph fallback regressions
run_batch.py drives run_pytest.sh per operator across GPUs (stable-hash sharding, per-op subprocess isolation, process-group timeouts, retry with deterministic-failure cutoff, two-level dtype fallback, .complete resume, per-op REPLAY_FROM). batch_summary.py aggregates run.log tables into summary.csv. ops/ holds the curated assets: dual-repo inventories rebuilt via AST scan + pytest collect verification, shape sets migrated from the old regression harness and merged with upstream core_shapes class-name keys (upstream's set_shapes falls back op_name -> MRO class name -> 1-D DEFAULT_SHAPES, so replacing the shape file without class keys crashes the BLAS family), and a dismiss list where all 76 entries carry verified reasons. Validated end to end: 1036-op full screen with zero failures. Also fix two cudagraph plugin regressions: newer torch appends "enable device-side assertions" to every CUDA error, so the loose fatal-error marker disabled the documented do_bench fallback entirely; and an aborted graph capture can leave the default CUDA RNG generator stuck in capturing state, poisoning every later torch.randn - captures now run under a throwaway RNG state. run_pytest.sh gains an optional DTYPES passthrough.
This commit is contained in:
+40
-5
@@ -8,7 +8,9 @@ fall back to plain do_bench and print a BENCHMARK_DIRECT_NO_CUDAGRAPH marker
|
||||
into the log. Before capturing, fn is explicitly warmed up for the caller's
|
||||
warmup time budget (do_bench_cudagraph's own 5-iter warmup is too short to
|
||||
settle autotune/JIT for the MoE kernels, which makes the first captured
|
||||
measurement unstable run-to-run).
|
||||
measurement unstable run-to-run). The capture itself runs under a scratch CUDA
|
||||
RNG state so that an aborted capture cannot poison the process-wide generator
|
||||
(rationale: _capture_with_scratch_rng).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -25,13 +27,19 @@ _ORIGINAL_DO_BENCH = _tt.do_bench
|
||||
def _is_fatal_cuda_error(exc: Exception) -> bool:
|
||||
"""True for CUDA context errors (illegal address, OOM, device-side assert):
|
||||
the context is poisoned, so re-raise rather than fall back and record a bogus
|
||||
latency as success."""
|
||||
latency as success.
|
||||
|
||||
Markers must match the error text itself, not torch's generic footer: newer
|
||||
torch appends "Compile with `TORCH_USE_CUDA_DSA` to enable device-side
|
||||
assertions." to EVERY AcceleratorError, so a loose "device-side assert"
|
||||
marker turned all capture-incompatible ops into fatal re-raises and killed
|
||||
the documented do_bench fallback path entirely."""
|
||||
msg = f"{type(exc).__name__}: {exc}".lower()
|
||||
markers = (
|
||||
"illegal memory access",
|
||||
"cudaerrorillegaladdress",
|
||||
"out of memory",
|
||||
"device-side assert",
|
||||
"device-side assert triggered",
|
||||
"an illegal instruction",
|
||||
"misaligned address",
|
||||
"uncorrectable ecc",
|
||||
@@ -83,6 +91,33 @@ def _warmup_before_capture(fn, warmup_ms, grad_to_none):
|
||||
torch.cuda.synchronize()
|
||||
|
||||
|
||||
def _capture_with_scratch_rng(call):
|
||||
"""Run a graph capture under a throwaway CUDA RNG state.
|
||||
|
||||
torch.cuda.graph's capture_begin unconditionally registers the default CUDA
|
||||
RNG generator and sets its capturing flag; when a capture aborts midway the
|
||||
epilogue may never run, the flag sticks, and every later CUDA RNG call in
|
||||
the process (the next shape's torch.randn included) raises "Offset
|
||||
increment outside graph capture". Repairing after the fact is unreliable —
|
||||
when the poisoning surfaces depends on which stage the capture died in —
|
||||
so prevent it instead: swap in a scratch state via graphsafe_set_state
|
||||
before capturing and restore the original state object afterwards, success
|
||||
or failure. The real generator never takes part in a capture, so poisoning
|
||||
can only land on the discarded scratch state, and _seed_plugin determinism
|
||||
is left untouched."""
|
||||
import torch
|
||||
dev = torch.cuda.current_device()
|
||||
gen = torch.cuda.default_generators[dev]
|
||||
saved = gen.graphsafe_get_state()
|
||||
scratch = torch.Generator(device="cuda")
|
||||
scratch.manual_seed(gen.initial_seed())
|
||||
gen.graphsafe_set_state(scratch)
|
||||
try:
|
||||
return call()
|
||||
finally:
|
||||
gen.graphsafe_set_state(saved)
|
||||
|
||||
|
||||
def _patched_do_bench(fn, warmup=25, rep=100, grad_to_none=None,
|
||||
quantiles=None, return_mode="mean"):
|
||||
"""Drop-in replacement for triton.testing.do_bench using cudagraph capture."""
|
||||
@@ -100,13 +135,13 @@ def _patched_do_bench(fn, warmup=25, rep=100, grad_to_none=None,
|
||||
# Settle autotune/JIT before capture (rationale: _warmup_before_capture).
|
||||
_warmup_before_capture(fn, warmup, grad_to_none)
|
||||
phase = "capture"
|
||||
return _tt.do_bench_cudagraph(
|
||||
return _capture_with_scratch_rng(lambda: _tt.do_bench_cudagraph(
|
||||
fn,
|
||||
rep=rep,
|
||||
grad_to_none=grad_to_none,
|
||||
quantiles=quantiles,
|
||||
return_mode=return_mode,
|
||||
)
|
||||
))
|
||||
except Exception as exc:
|
||||
# Re-raise genuine device failures; anything else falls back to plain
|
||||
# do_bench, with the failing phase (sync/warmup/capture) in the marker.
|
||||
|
||||
Reference in New Issue
Block a user