(fix): Compile-Time Explosion in @inline @with_pool + Binary RUNTIME_CHECK#31
(fix): Compile-Time Explosion in @inline @with_pool + Binary RUNTIME_CHECK#31
@inline @with_pool + Binary RUNTIME_CHECK#31Conversation
Two-tier approach based on STATIC_POOL_CHECKS: 1. STATIC_POOL_CHECKS=false (production): generate S=0 only branch in _wrap_with_dispatch, eliminating 4-branch code duplication. Cuts single-function TTFX ~50% (2.0s → 0.8s) and prevents exponential 4^N blowup in nested @inline @with_pool (64s → 1.4s at 3 levels). 2. STATIC_POOL_CHECKS=true (debug): inject :noinline meta into generated @with_pool function definitions to prevent inlining of 4-branch dispatch bodies into callers. Changes 4^N (exponential) scaling to 4*N (linear) for nested calls. Runtime cost (~2-5ns/call) is negligible in debug mode.
…E_CHECK Collapse the old 4-level safety system (off/guard/full/debug) into a binary S=0 (off) / S=1 (full checks) system controlled by a single RUNTIME_CHECK Bool flag. Key changes: - New RUNTIME_CHECK compile-time Bool (LocalPreferences: runtime_check) - _make_pool / _make_cuda_pool: 4 branches → 2 branches - _dispatch_pool_scope: 4 branches → 2 branches - _wrap_with_dispatch: 4-branch loop → 2-branch (S=0 fast path + S=1) - All S >= 2 / S >= 3 thresholds → S >= 1 - STATIC_POOL_CHECKS → RUNTIME_CHECK in macro codegen - Backward-compatible aliases preserved (DEFAULT_SAFETY_LV, STATIC_POOL_CHECKS, POOL_SAFETY_LV) - set_safety_level! range narrowed to 0-1 - Legacy path (Julia ≤1.10) fully mirrored - CUDA extension updated (types.jl + debug.jl) Motivation: intermediate safety levels (1=guard, 2=full) had negligible overhead difference (~30ns) and limited practical value — users either want zero overhead or full diagnostics. Binary simplification also reduces worst-case TTFX from 4^N to 2^N for nested @with_pool.
…fety artifacts
Complete the binary RUNTIME_CHECK migration (S=0/1 only):
- Remove POOL_DEBUG Ref{Bool} toggle and all || POOL_DEBUG[] gates
- Remove set_runtime_check!, _dispatch_pool_scope, _set_cuda_runtime_check_hook!
- Eliminate union splitting in _wrap_with_dispatch — now emits compile-time
const type assertion (pool::PoolType{RUNTIME_CHECK ? 1 : 0})
- Fix stale multi-level thresholds: S>=2 (poisoning) and S>=3 (borrow tracking)
collapsed to S>=1 — all safety features activate together
- Update all tests to use _make_pool(true/false) with direct checkpoint/rewind
instead of set_runtime_check! + @with_pool
BREAKING: POOL_DEBUG and set_runtime_check! removed from public API.
Use RUNTIME_CHECK preference (compile-time) or _make_pool(true) for testing.
- RUNTIME_CHECK: Bool → Int (0=off, 1=on), extensible like -O0/-O1/-O2
- LocalPreferences.toml accepts both Bool and Int, normalized via
_normalize_runtime_check
- Macro simplified: RUNTIME_CHECK ? 1 : 0 → direct RUNTIME_CHECK
- _runtime_check: {0}→false, generic {S}→true (future-proof for S>1)
- _make_pool: accepts both Bool and Int
- CUDA test_cuda_safety.jl: full rewrite from 4-tier (S=0/1/2/3) to
binary (S=0/1), removes all POOL_DEBUG/set_safety_level!/
_safety_level references
Replace all references to removed APIs (POOL_DEBUG, set_safety_level!, multi-level 0-3 system) with the new binary RUNTIME_CHECK (0=off, 1=on) compile-time preference. Key changes across 5 doc files: - safety.md: full rewrite — unified poisoning/invalidation/escape/borrow under RUNTIME_CHECK=1, removed 4-tier level table - configuration.md: added runtime_check preference section with Bool/Int acceptance, removed POOL_DEBUG section, added restart warning - safety-rules.md: replaced POOL_DEBUG debugging section with LocalPreferences.toml example - multi-threading.md: updated debugging tips and summary - api.md: POOL_DEBUG → RUNTIME_CHECK in configuration table
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #31 +/- ##
==========================================
+ Coverage 96.97% 97.20% +0.23%
==========================================
Files 14 14
Lines 2645 2613 -32
==========================================
- Hits 2565 2540 -25
+ Misses 80 73 -7
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
This PR fixes exponential compile-time blowup (4^N branches from union splitting) when @inline @with_pool is nested 3+ levels deep, by replacing the 4-tier runtime-togglable safety system with a binary compile-time constant RUNTIME_CHECK.
Changes:
- Replaced
POOL_SAFETY_LV(runtimeRef{Int}, levels 0-3),POOL_DEBUG,set_safety_level!(), and_dispatch_pool_scope(closure-based union splitting) with a singleRUNTIME_CHECKcompile-timeconst Int(0=off, 1=on) and direct type assertionpool::PoolType{RUNTIME_CHECK}in macros. - Consolidated all safety features (structural invalidation, poisoning, escape detection, borrow tracking) into a single S=1 level, removing the multi-tiered threshold system.
- Updated all tests to use direct
_make_pool(true/false)construction and manual_lazy_checkpoint!/_lazy_rewind!instead ofset_safety_level!+@with_poolmacro.
Reviewed changes
Copilot reviewed 34 out of 34 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types.jl, src/legacy/types.jl | Replace POOL_SAFETY_LV/DEFAULT_SAFETY_LV/STATIC_POOL_CHECKS with RUNTIME_CHECK const; simplify _make_pool to binary dispatch; replace _safety_level with _runtime_check returning Bool |
| src/macros.jl | Replace closure-based _dispatch_pool_scope union splitting with direct pool::PoolType{RUNTIME_CHECK} type assertion; replace _safety_level >= N || POOL_DEBUG[] guards with _runtime_check(pool) |
| src/task_local_pool.jl | Remove _dispatch_pool_scope, set_safety_level!, _set_cuda_safety_level_hook! |
| src/state.jl, src/legacy/state.jl | Remove default S parameter from _rewind_typed_pool!, _invalidate_released_slots!, reset!; merge poisoning into S≥1 level |
| src/debug.jl | Remove POOL_DEBUG ref; simplify showerror to remove multi-level labels and "Tip: set LV=3" text |
| src/utils.jl | Replace _safety_label with _check_label; update display to use check=on/off |
| src/AdaptiveArrayPools.jl | Remove exports: POOL_DEBUG, POOL_SAFETY_LV, STATIC_POOL_CHECKS, DEFAULT_SAFETY_LV, set_safety_level!, deprecated aliases |
| ext/.../types.jl | CUDA: binary _make_cuda_pool, _runtime_check for CuAdaptiveArrayPool; remove _set_cuda_safety_level_hook! |
| ext/.../debug.jl | CUDA: replace S >= 2 || POOL_DEBUG[] with S >= 1 |
| ext/.../state.jl | CUDA: pass S to reset! calls |
| ext/.../macros.jl | CUDA: update comments for direct type assertion |
| ext/.../task_local_pool.jl | Remove _set_cuda_safety_level_hook! |
| test/test_safety.jl | Rewrite tests for binary S=0/S=1; use _make_pool(true/false) |
| test/test_debug.jl | Remove POOL_DEBUG tests; rewrite escape detection tests to use direct _make_pool + _validate_pool_return |
| test/test_borrow_registry.jl | Rewrite borrow tracking tests for binary system |
| test/cuda/test_cuda_safety.jl | Consolidate CUDA safety tests to binary S=0/1 |
| test/test_task_local_pool.jl | Replace set_safety_level! tests with _make_pool tests |
| test/test_state.jl | Pass S argument to reset! and _rewind_typed_pool! |
| test/test_allocation.jl | Remove set_safety_level! save/restore |
| docs/ | Update safety, configuration, safety-rules docs for binary RUNTIME_CHECK |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Problem
@inline @with_poolnested 3+ levels deep caused exponential compile-time blowup (4^N branches from union splitting). A 3-level nesting took 64 seconds to compile vs expected ~1s.Root cause: the macro generated 4-branch dispatch (
S=0,1,2,3) that got inlined and multiplied at each nesting level.Solution
Replace the 4-tier runtime-togglable safety system with a binary compile-time constant. One type parameter, one branch — no union splitting, no exponential blowup.
@with_poolpool::Pool{RUNTIME_CHECK}set_safety_level!(N)at runtimeLocalPreferences.toml+ restart34 files changed, +815 -1530 (net -715 lines)
What Changed
Removed
set_safety_level!(),set_runtime_check!(),POOL_DEBUG— runtime togglesS >= 2poisoning,S >= 3borrow tracking)Added
RUNTIME_CHECK::Int— compile-time const,0= off,1= all checksruntime_checkinLocalPreferences.toml(acceptstrue/1)_runtime_check(pool) -> Bool—{0}→false,{S>=1}→truepool::PoolType{RUNTIME_CHECK}(single branch)Configuration
Migration (minor — safety API only)
These APIs were added recently and have no downstream users yet.
set_safety_level!(2)runtime_check = 1in LocalPreferences.toml + restartPOOL_DEBUG[] = trueruntime_check = 1instead)For full details on the new safety system, see the updated Safety documentation.