Skip to content

pgw#1507: the varena facade — serve a real pipeline out of the arena behind the pgw#1497 planner contract - #1068

Merged
PaulFidika merged 3 commits into
masterfrom
1507-varena-facade
Aug 20, 2026
Merged

pgw#1507: the varena facade — serve a real pipeline out of the arena behind the pgw#1497 planner contract#1068
PaulFidika merged 3 commits into
masterfrom
1507-varena-facade

Conversation

@PaulFidika

@PaulFidika PaulFidika commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Roadmap phase 1 remainder. varena's primitives were merged and verified; nothing
served through them. This makes an arbitrary diffusers nn.Module tree hold its
weights in arena Reservations, behind the SAME planner/profile contract
models/stream_residency.py shipped in pgw#1497 — so the rung swaps without an
endpoint noticing.

varena pin: master 9b287a4, built with maturin build --release into a
cp312 wheel and installed into the worktree venv beside torch 2.13.0+cu130.
The varena repo is UNMODIFIED — no substrate change was needed or asked for,
which is the minimalism ruling working.

Read this before the diff: the headline number is a LOSS

RTX 4070, sd1.5, 512x512, 25 steps, CFG 7.5, fp16, seed 1507, latents compared
with torch.equal. One config, warmup, best-of-2, load 3.17 at start. Raw JSON
in benchmarks/pgw1507/.

arm ms/step x native device peak pgw#1497 software rung
eager resident 107.6 1.00 2.68 GiB 1.00 (119.6 ms)
arena resident-equivalent 110.0 1.02 3.10 GiB -
arena @50% 302.3 2.81 1.94 GiB 1.91
arena @25% 425.5 3.95 1.49 GiB 3.16
arena @5% 454.5 4.22 1.24 GiB 3.56

The arena is ~25% slower than the software rung at every streaming budget, and
free (1.02x) when nothing streams. Every arm bitwise identical to eager.

The cause, isolated — and the competing explanation falsified first.
Micro-benchmark on the same card: back(4MiB)+unback(4MiB) 97.8 us; back() on
an already-backed range 0.2 us; DLPack export + from_dlpack 6.1 us; H2D 4 MiB
pinned->arena 376.0 us vs pinned->ordinary torch buffer 368.5 us; signature()
0.1 us. So arena memory is not slower for DMA, and the whole gap is ~98 us of
page-table work per streamed leaf per forward (at 25%: ~464 page-ins/step x
98 us = 45 ms/step against a measured 60 ms/step gap). The obvious alternative —
the unback ring's CPU-side event block serialising the pipeline — was killed
before this was believed: ring depth 2/4/8/16 gives 3.56x / 3.74x / 4.07x /
3.83x. Deeper is worse.

Second cost, predicted CPU-side and confirmed to the byte: the granularity tax
is 248 MiB = 13.2%
. It is charged to the planner (regions priced at their
aligned span), so plan.resident_bytes equals bytes actually mapped and
plan.fits is exact — but the arena holds 13% less weight at the same lease.

Where it wins, and what it is actually for

  • Cold load, page cache EVICTED (fadvise DONTNEED): loader 3.89 s
    (0.44 GB/s) vs RefillEngine->arena 3.03 s (0.57 GB/s) = 1.28x, because the
    meta-init tree never allocates a torch tensor for the weights at all.
    Byte-exact: 0 mismatched of 686 UNet tensors, warm and evicted.
  • 1.02x resident-equivalent — the regime phase 3 lives in, for 2%.
  • Stable virtual addresses across demote/promote/rebudget. That is the
    deliverable, and the precondition for "one compiled artifact serves every
    budget".

Design input for phase 3, not acted on here: a compiled cell must not page the
page table per forward at leaf granularity. Coarser cells amortise both the
98 us map and the 13.2% tax.

Identity, cycles, LoRA

Bitwise identical throughout. Three demote/promote cycles between requests:
mapped 2122 -> 66 -> 2122 MiB, signature 1->239->239, 239->477->477,
477->715->715 (advances on demote, stable across promote — back() is
signature-neutral), every resident region confirmed backed by asking the
driver, output still bitwise identical after each re-promotion. LoRA: 96
attached pairs -> 192 adapter leaves, all in the always-resident core, none ever
streamed, 36 of their base layers streaming, identical at full/25%/5%.

Three defects the card found

  1. pipeline.device again — the facade parked leaves on the host without
    stamping the handle pgw#1497's repair reads; sd1.5 died with index is on cpu. ArenaResidency.arm() is now the production entry point and discharges
    all three of _apply_partial_stream's obligations. The repair itself needed
    no change: it asks the stamped handle where the model executes.
  2. Parameter.data = across the meta boundary is refused by torch and killed
    the cold-load leg. A meta parameter is replaced, not filled; every other bind
    still preserves identity.
  3. 239 submit/wait round trips on cold adoption drained the engine queue
    each time (0.83 s). One batch, both destinations: 0.37 s.

Boundaries held

compiled-offload exclusivity UNTOUCHED — it is never even consulted, because
no arena rung is on the LADDER, deliberately: a rung takes a measured price,
and the measured price is worse than partial_stream's at every streaming
budget. It goes on the ladder when phase 3 gives it the thing it is for. No NVML,
no probe, no prefetch, no eviction heuristic. {VRAM, RAM} pair shape is exactly
what pgw#1497 landed — with one thing moved FORWARD, pgw#1497's own owed item: a
stated RAM half of zero now means the tail pages off disk through RefillEngine
instead of refusing.

18 CPU tests over real nn.Module trees and real safetensors files, all
red-armed; 58 green with pgw#1497's. ruff + mypy clean.

Named deferrals: SDXL still unmeasured (as pgw#1497 left it); serving-path rung
wiring (above); disk floors belong to va#3 Tier 1 (the evicted arms varied
3.89 s vs 15.06 s on this shared box — relative comparison only).

@PaulFidika
PaulFidika marked this pull request as ready for review August 19, 2026 23:40
…as the mechanism

pgw#1497 shipped the CONTRACT (plan_residency, the {VRAM, RAM} pair,
engage/rebudget/partial_unload/demote_to_host) with ONE mechanism behind it: a
pinned-CPU tail cast per forward into a shared ring buffer. This is the SECOND
mechanism behind the SAME contract.

`models/arena_residency.py`:

* **One reservation, stable addresses.** One varena Arena per model holds the
  lease's VRAM half; one Reservation holds a virtual range for every managed
  weight and that range never moves. Resident leaves are `back()`-ed and the
  module's parameter is a zero-copy DLPack view; streamed leaves are unbacked
  and cost zero VRAM. Promote/demote/rebudget move PHYSICAL PAGES under a fixed
  pointer — which is the property phase 3 needs to lift compiled⊥offload, and
  which is NOT lifted here.

* **The granularity tax is charged to the planner, not discovered later.** CUDA
  VMM maps in 2 MiB chunks, so independent per-leaf back/unback requires
  per-leaf granularity-aligned regions — two leaves sharing a chunk could never
  release it, and would race if one were in flight. The layout therefore prices
  each region at its ALIGNED SPAN, so `plan.resident_bytes` equals the bytes the
  arena really maps and `plan.fits` is exact instead of optimistic in the
  direction that OOMs. `ArenaLayout.tax_bytes` names the total; leaves that can
  never stream are packed into ONE core region and pay one chunk remainder
  between them instead of one each (226 sd1.5 unet leaves, 67 MB, one remainder).

* **UnbackRing — the correctness point.** A post-hook fires when the leaf's
  kernels are ENQUEUED, not when they have run, so unbacking there unmaps memory
  a live kernel is reading. The post-hook records a CUDA event and the region
  joins a `depth`-deep ring; the unback happens when a later leaf needs the room,
  after that event completed. `depth` IS the planner's `streams`, so the ring
  holds exactly the in-flight window `plan_residency` already reserved.

* **Two refill sources, chosen by the RAM half, not by a flag.** Host mirror
  present -> pinned H2D on fenced offload streams. No mirror (`host_mirror=False`,
  i.e. a stated RAM half of zero) -> straight off disk through varena's
  RefillEngine. That is pgw#1497's owed RAM-half enforcement arriving as a
  DEGRADE rather than a refusal. A meta-initialised tree is COLD, not broken:
  its weights go disk -> arena and never occupy a torch allocation at all.

* **`safetensors_triples` is the seam.** (path, offset, len) from the header's
  own data_offsets, no tensor materialised. pgw#1498's CAS-native ingest arrives
  as a different producer of the same dict — one constructor argument.

* LoRA semantics are identical to the streamed rung because they are the SAME
  code: adapters are detected by `discover_leaves` and land in the always-resident
  core, exactly as pgw#1497 forces them resident.

`models/stream_residency.py`: the leaf walk, the tensor helpers and the 512-byte
alignment are now public and SHARED (`discover_leaves`, `own_tensors`,
`bind_tensor`, `is_streamable_leaf`, `is_adapter_name`, `tensor_bytes`,
`aligned`). Two mechanisms behind one contract must agree leaf-for-leaf or their
plans are not comparable, and a second copy of that walk is how they would
silently stop agreeing. No behaviour change; pgw#1497's 20 tests still pass.

Deliberately absent, per varena's minimalism ruling applied to its caller: no
NVML, no probe, no prefetch policy, no eviction heuristic. The budget is the
lease's and the split is pgw#1497's planner. varena itself is UNMODIFIED (pinned
at master 9b287a4).

16 tests over REAL nn.Module trees and REAL safetensors files, every one
red-armed: unaligned spans -> 2 red; +1 on the triple offset -> 1 red; no core
packing -> 4 red. ruff + mypy clean.

GPU legs (bitwise identity, demote/promote cycles, the pricing rerun, cold load)
follow in the next commit — the card is arbitrated and this half is CPU-only.
…exists for is a LOSS on the streaming axis

RTX 4070, sd1.5, 512^2, 25 steps, CFG 7.5, fp16, seed 1507, latent output
compared with `torch.equal`. ONE config, warmup, best of 2, load 3.17 at start.
Raw JSON in `benchmarks/pgw1507/`.

## Identity and cycles — clean

Eager resident vs arena resident-equivalent: BITWISE IDENTICAL. Three
demote/promote cycles BETWEEN requests: mapped 2122 -> 66 -> 2122 MiB each
time, signature 1->239->239, 239->477->477, 477->715->715 (advances on demote,
stable across promote — `back()` is signature-neutral, as varena#1 claims),
every resident region confirmed backed by asking the DRIVER rather than our own
bookkeeping, and the output after each re-promotion still bitwise identical.

LoRA: 96 attached pairs -> 192 adapter leaves, ALL in the always-resident core,
ZERO ever given a streamable region, while 36 of their base layers DO stream.
Bitwise identical at full / 25% / 5%. Identical to the streamed rung because it
is the same `discover_leaves`.

## The pricing table

| arm | ms/step | x native | device peak | pgw#1497 software rung |
|---|---|---|---|---|
| eager resident | 107.6 | 1.00 | 2.68 GiB | 1.00 (119.6 ms) |
| arena resident-equivalent | 110.0 | **1.02** | 3.10 GiB | - |
| arena @50% | 302.3 | **2.81** | 1.94 GiB | **1.91** |
| arena @25% | 425.5 | **3.95** | 1.49 GiB | **3.16** |
| arena @5% | 454.5 | **4.22** | 1.24 GiB | **3.56** |

The arena is ~25% SLOWER than the software rung at every streaming budget, and
free (1.02x) when nothing streams. Stated plainly because it is the number the
issue asked for.

## Why — measured, and the alternative explanation falsified first

Micro-benchmark, same card: `back(4MiB)+unback(4MiB)` 97.8 us; `back()` on an
already-backed range 0.2 us; DLPack export + `from_dlpack` 6.1 us; H2D 4 MiB
pinned->arena 376.0 us vs pinned->ordinary torch buffer 368.5 us; `signature()`
0.1 us. So **arena memory is not slower for DMA** and the whole gap is ~98 us of
page-table work per streamed leaf per forward: at 25%, ~464 page-ins/step x
98 us ~= 45 ms/step against a measured gap of 60 ms/step.

The obvious competing explanation — the ring's CPU-side event block serialising
the pipeline — was FALSIFIED before this one was believed: sweeping ring depth
(streams 2/4/8/16) at the 25% budget gives 3.56x / 3.74x / 4.07x / 3.83x.
Deeper is WORSE, because a deeper ring only reserves more in-flight window and
evicts more resident leaves.

Second cost, predicted CPU-side and confirmed to the byte: the granularity tax
is 248 MiB = 13.2% (1.830 GiB of weights in 2.072 GiB of span, 239 regions).
Charged to the planner, so `plan.resident_bytes` is exact — but the arena holds
13% less WEIGHT than the software rung at the same lease.

Contract-level demote/promote, both mechanisms, same tree: arena 744 ms demote /
232 ms promote (2.008 GiB) vs software 630 / 157 ms (1.768 GiB) — per GiB
370/116 vs 356/89 ms, again the extra bytes plus the map cost.

## Cold load — the arm the arena WINS

Byte-exact: 0 mismatched of 686 UNet tensors, page-cache warm AND evicted
(`fadvise DONTNEED`; a "cold load" measured with the file in RAM measures
memcpy). Warm: loader 0.31 s (5.49 GB/s) vs RefillEngine->arena 0.37 s. EVICTED:
loader 3.89 s (0.44 GB/s) vs arena 3.03 s (0.57 GB/s) — **1.28x, because the
meta-init tree never allocates a torch tensor for the weights at all.**

## Three defects the card found

1. **`pipeline.device` again.** The facade parked leaves on the host without
   stamping the handle pgw#1497's execution-device repair reads, so sd1.5 died
   in the first embedding with `index is on cpu`. Fixed where it belongs:
   `ArenaResidency.arm()` is the production entry point and discharges all three
   of `_apply_partial_stream`'s obligations — exclude AND place the unhookable
   components, filter the component vocabulary, stamp the handle and install the
   repair. The repair itself needed no change: it asks the handle where the model
   executes, so a second mechanism gets it by being stamped.
2. **`Parameter.data =` across the meta boundary.** torch REFUSES it
   ("incompatible tensor type") and the cold-load leg died there. A meta
   parameter has never held a byte and has no identity worth preserving, so it
   is REPLACED; every other bind still fills `.data` and preserves identity,
   which is what keeps hooks and LoRA wrappers working across promotes.
3. **239 submit/wait round trips.** Adopting a cold tree region by region drains
   the engine queue to empty each time, so io_uring's depth buys nothing — 0.83 s.
   `_adopt_cold` hands the engine every triple in ONE batch, both destinations at
   once: 0.37 s. That batched shape is also what pgw#1498's CAS ingest wants.

18 CPU tests (2 new: the LoRA clause and the meta-bind rule), 58 green with
pgw#1497's. ruff + mypy clean. varena UNMODIFIED, pinned at master 9b287a4.
…med for its DOMAIN, and the unreached facade DECLARES its owner

Three CI findings, all correct, none worked around.

1. **mypy strict over tests.** `torch = pytest.importorskip("torch")` binds a
   VARIABLE, so `torch.Tensor` in an annotation is not a type at all. Annotations
   dropped rather than a stub added; `specs_for` got its return type.

2. **`lint_incident_test_names` (DESIGN-RULINGS 4.34b).** `test_arena_layout_pgw1507.py`
   said WHEN it was written, not WHAT it exercises. Renamed to
   `tests/test_arena_residency.py` — the domain — with the lineage kept as a
   one-line comment where the ruling says to keep it. The baseline list only
   shrinks, so adding a row was never an option and the guard is right that it
   should not have been.

3. **`lint_unreached_surface`.** `ArenaResidency` and `safetensors_triples` have
   no non-test call site, and the gate is TELLING THE TRUTH: nothing in `src/`
   reaches the facade. That is a DECISION and it is now written down where the
   guard demands — an `EXEMPT_TARGETS` row naming the owner of the wiring, which
   the file's own contract calls "a DELETION THAT IS OWED, never a permanent
   pardon". A ladder rung takes a MEASURED price and the measured price is worse
   than `partial_stream`'s at every streaming budget, so putting it on the ladder
   now would add a rung `select_auto_mode` must never choose. It gets a
   production caller when roadmap phase 3 gives it the thing its stable virtual
   addresses are for; if phase 3 is abandoned those two rows are the deletion
   notice for the module.

PRE-EXISTING and NOT MINE, stated so nobody reads this PR's gate colour as its
own verdict: `fast gates` is red on `origin/master` and on every recent PR
(#1061, #1063, #1066, #1067 all show `fast gates: fail`). Reproduced locally on
this branch: `lint_unreached_surface` still reports six NEW unreached symbols in
`models/gguf_torch` — which is exactly what pgw#1498's own tracker entry says
about itself ("the module is correct but NOT REACHABLE") — and
`lint_incident_test_names` still reports pgw#1497's, #1498's and #1499's
issue-named modules. This branch adds no new violation of either.
@PaulFidika
PaulFidika merged commit 11bf60b into master Aug 20, 2026
1 of 2 checks passed
@PaulFidika
PaulFidika deleted the 1507-varena-facade branch August 20, 2026 00:19
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