Skip to content

(perf): GPU parity — depth-tagged touched-others stack, PoolCheckpointState, lookup memo (CUDA/Metal)#52

Merged
mgyoo86 merged 10 commits into
masterfrom
refactor/gpu-parity
Jul 10, 2026
Merged

(perf): GPU parity — depth-tagged touched-others stack, PoolCheckpointState, lookup memo (CUDA/Metal)#52
mgyoo86 merged 10 commits into
masterfrom
refactor/gpu-parity

Conversation

@mgyoo86

@mgyoo86 mgyoo86 commented Jul 10, 2026

Copy link
Copy Markdown
Member

Problem

The GPU extensions still ran the pre-#50 design: _lazy_checkpoint! eagerly snapshotted every registered fallback type at scope entry (O(registered types) — the pollution bug #50 fixed on CPU), lazy rewinds iterated the whole values(pool.others) dict, and every fallback acquire paid an IdDict lookup. #51's reorder deferred GPU parity so both backends could mirror the final design once.

Fix

Mirror #51's architecture in both extensions, sharing one implementation where possible:

  1. Shared stack helpers_touch_fallback_pool! / _drain_touched_others! / _truncate_touched_others! generalized over AbstractArrayPool (+ _check_level); CPU, Metal, and CUDA now run the same code. Compile-time _runtime_check gating keeps the S=0 DCE contract (verified via @code_typed: zero dynamic dispatch, branch fully folded).
  2. PoolCheckpointState on GPU typed poolsMetalTypedPool/CuTypedPool nest their checkpoint fields into the same concrete struct as CPU (property forwarding keeps all call sites source-compatible), so the S=0 drain runs zero-dispatch on Vector{PoolCheckpointState}.
  3. Depth-tagged stack + lookup memo — eager snapshots deleted; producers push one (state, depth) entry per first touch; lazy rewinds drain by depth tag; full checkpoint/rewind keep their eager-sweep + truncate pairing. get_typed_pool! memoizes the last fallback lookup.

Float16 caveat (GPU-only divergence): Float16 is a fixed slot with _fixed_slot_bit == 0 (bit-7 reassignment), so stack routing uses !(T <: _*_FIXED_TYPES) — never the bit test. Routing it onto the stack would double-rewind against the lazy rewinds' Float16 special case and corrupt parent-scope counts (caught in plan review; regression testsets guard it on both backends).

Also: dead _make_cuda_pool(level, old)/_transfer_cuda_pool removed (zero callers; same rationale as #51's removal — and its positional constructor would no longer compile). Surfaced along the way and fixed: GPU poison-fill now mirrors the CPU best-effort contract (never throws during rewind on zero-less isbits types), and GPU/auto-manage tests are runtime_check=1-clean (S-adaptive asserts; scopes never return pool-backed arrays).

Benchmarks

Metal (Apple Silicon, local), lazy @with_pool :metal scope acquiring one fallback type — same method as #50/#51:

1 registered type 8-type/1-type ratio
master ~390 ns 8.2× (eager snapshot is O(others))
this PR ~105 ns ≈1.00 flat for others = 1..13

Same-process registry-size-isolated sweep is the signal (a fresh-process JIT ordering artifact adds a ~50 ns constant offset unrelated to registry size — details in the branch notes). Zero-dispatch drain confirmed on the S=0 path.

Testing

  • test/metal/test_touched_others.jl + CUDA mirror (~850 lines): pollution regression, f_outer exception-leak recovery, depth-tag exactness/monotonicity, memo lifecycle (empty! clears / reset! preserves), Float16 non-interaction (lazy, typed, mixed), R=1 three-stack lockstep (happy path, exception unwind, nested depths), mixed fixed+fallback tuples.
  • Metal: full suite green on-device. CPU: full suite green (shared-helper refactor pinned by the existing touched-others + zero-alloc testsets).
  • CUDA: line-audited transcription of the reviewed Metal diff (normalized cross-diff, syntax-gated) — functional run on CUDA hardware done separately before merge.

Follow-ups (tracked)

  • PR4 macro work: Foo{Float64} static widening, per-scope tp hoisting, and an expansion-time escape lint for the two syntactic footguns (direct acquire / x .= v tails).

mgyoo86 added 10 commits July 9, 2026 22:04
Mirrors the CPU _poison_fill! contract: custom isbits element types without
zero(T) skip the poison pass instead of throwing MethodError mid-rewind.
Surfaced by the touched-others struct fallback types under runtime_check=1.
…nothing

At S=1, rewind's invalidation poisons released slots and compact! deliberately
defers their shrink to preserve the poison (see _maybe_compact_slot!) — the
capacity asserts now encode both semantics. Blocks also stop returning
pool-backed arrays (PoolEscapeError under runtime_check=1).
Same S=1 hygiene as the auto-manage tests: a @with_pool block whose last
expression is a pool-backed array throws PoolEscapeError under
runtime_check=1. All blocks now end with nothing; the @allocated / pointer
comparisons are unaffected (they never used the scope return value).
NOT run locally per user request — verify in the next full suite run.
@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 57.14286% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.81%. Comparing base (bc657a1) to head (760d0fd).

Files with missing lines Patch % Lines
src/state.jl 50.00% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #52      +/-   ##
==========================================
- Coverage   95.83%   95.81%   -0.03%     
==========================================
  Files          16       16              
  Lines        3677     3678       +1     
==========================================
  Hits         3524     3524              
- Misses        153      154       +1     
Files with missing lines Coverage Δ
src/types.jl 87.23% <100.00%> (+0.13%) ⬆️
src/state.jl 96.64% <50.00%> (-0.20%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mgyoo86
mgyoo86 merged commit 61cadd1 into master Jul 10, 2026
13 of 14 checks passed
@mgyoo86
mgyoo86 deleted the refactor/gpu-parity branch July 11, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant