Skip to content

Layer split auto: extract the planner into layer_split.hpp, pin the whole arena outside Windows - #294

Closed
giostrives wants to merge 3 commits into
Niko1221:mainfrom
giostrives:feature/multi_planner-0.1.30
Closed

giostrives wants to merge 3 commits into
Niko1221:mainfrom
giostrives:feature/multi_planner-0.1.30

Conversation

@giostrives

Copy link
Copy Markdown
Contributor

This PR is scavenged from #223, limiting the changes to the memory pinning and multi-gpu planner.

  • Moves the --layer-split auto cost model out of generate.cpp into a header-only, unit-tested Planner
  • Adds a hand-off cost to the model and stops capping the expert-arena pin on Linux.

Changes

  • Planner extracted (include/strata/program/layer_split.hpp). The placement arithmetic is now in layer_split::Planner, Card, Costs and Placement, apart from the devices. generate.cpp only gathers per-card VRAM and SM/clock data and calls planner.best(cards).
  • Hand-off term. The predicted decode window now includes one hand-off per later card (STRATA_SPLIT_HANDOFF_MS, default 1.0 ms; about 1.1 ms measured on 2 x RTX 2080 Ti over PCIe gen3). The startup log's predicted ... ms per decode window includes it.
  • New tunables. STRATA_SPLIT_HANDOFF_MS and STRATA_SPLIT_MASS_EXP join STRATA_SPLIT_MISS_MS. Defaults are unchanged from the previous hard-coded values (190 ms, exponent 1.2).
  • Arena pinning. arena_pin_cap(several_contexts, wddm) caps the pin at 8 GiB only under WDDM with several contexts. Linux now pins the whole arena, as with one card.
  • Tests. New layer_split_test (registered in CMake) covers the pin cap, per-layer time, the fill-stops-at-first-misfit rule, hand-off terms, placement search for 2, 3 and 4 cards, and the carve pricing.
  • Docs. docs/MULTI_GPU.md describes the new model, the per-layer sessions and borrowed prompt buffers, and adds a 2 x RTX 2080 Ti measurement.

Measurements

Qwen3.8-Flash-Next IQ2_XS (33 GiB of experts, more than both cards hold), 2 x RTX 2080 Ti (11 GB, PCIe gen3 x16, i7-7700K), Linux, 32K context, --spec 4. Auto chose K=25.

Prompt 1.9K / 8K / 16K / 29K tok/s Decode tok/s Decode cache hits
one 2080 Ti 466 / 695 / 840 / 836 36.7 66%
2 x 2080 Ti, auto (K=25) 485-490 / 774-781 / 1,116-1,131 / 1,278-1,302 48.4-50.3 80-81%
same, arena pinned only to 8 GiB 442 / 696 / 921 / 999 44.7 -

Decode is +32-37%. Prompts gain with length: +4-5% at 1.9K, +53-56% at 29K.

giostrives and others added 3 commits September 30, 2026 21:10
A split capped the arena's pinned registration at 8 GiB on every platform. The
cap is a WDDM workaround (pinning all of it into two contexts left the driver
refusing later allocations on an RTX 5080 + RTX 3090), but on Linux it cost the
split the PCIe share of every missed expert past the first 8 GiB of layers,
and made its prompt path stage most experts through host copies. One GPU pins
all 33 GiB on the same machine.

The cap now applies under WDDM only (layer_split::arena_pin_cap, tested).
WSL2 is a Linux build on the WDDM driver, but its driver pins only ~1 GiB of
host memory in all: the whole registration is refused and the slices stop at
that budget, with or without the cap - as with one GPU there today.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-off term

The placement's cost model moves out of main() into layer_split::Planner:
the same fill (each card's layers' profiled pairs, hottest first, stopping at
the first that does not fit), the same search (every placement for two or
three cards, in proportion to speed beyond), the same per-layer time from
SMs x clock, and the session carve priced per placement (a card's room less
session_bytes of its layer range, passed in as range_bytes). For the same
inputs it picks the same split points as the inline search did.

Each later card now adds a hand-off per window (STRATA_SPLIT_HANDOFF_MS,
1 ms), so the predicted window counts what a card costs beyond its layers;
the routing curve's exponent is a parameter (STRATA_SPLIT_MASS_EXP), since
it differs between models. 16 checks in layer_split_test, 14 of them on a
toy model and three of those for the carve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rompt buffers); 2 x RTX 2080 Ti measured

What each card holds and the prompt-buffer limit still described the state
before Niko1221#216: every card now holds only its own layers' session and borrows
its prompt buffers from its own cache. Adds the IQ2_XS numbers on two RTX
2080 Ti cards, split against one card and against the 8 GiB arena pin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@TimShawi1233

Copy link
Copy Markdown

I need a web setup

@TimShawi1233

Copy link
Copy Markdown

Seed Phrase

@giostrives

Copy link
Copy Markdown
Contributor Author

I don't know what that comment is, I believe some kind of spam.

@Niko1221

Niko1221 commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Thanks! The pin part shipped in 0.1.31 (#253: whole arena outside WDDM, STRATA_ARENA_PIN_GIB). Please rebase the planner (layer_split.hpp + its test) without arena_pin_cap, with the new hand-off term defaulting to 0 (or show the chosen K is unchanged on the documented rigs). Closing until then.

@giostrives

Copy link
Copy Markdown
Contributor Author

Okay, rebased and applied the required changes:

Checked on 2 × RTX 2080 Ti (IQ2_XS) with STRATA_SPLIT_HANDOFF_MS at 0 and at 1: both choose K=25 (layers 0-24 / 25-47). Decode is 52.5 tok/s at 0 and 52.8 at 1. Prompts at 0, in tok/s: 604 (2K), 921 (8K), 1257 (16K), 1602 (30K). Needle found at every length. layer_split_test passes (16 checks).

2 x RTX 2080 Ti, IQ2_XS, --layer-split auto: K=25 (layers 0-24 / 25-47) with the hand-off at 0 and at 1.

STRATA_SPLIT_HANDOFF_MS Decode (tok/s) 2K prompt (tok/s) 8K prompt (tok/s) 16K prompt (tok/s) 30K prompt (tok/s)
0 (new default) 52.5 604 921 1257 1602
1 (previous) 52.8 609 924 1378 1592

On my machine, 1 ms is no worse, and at most slightly better (decode +0.6%, 16K prompt +9%; single runs, so within noise).

@giostrives

Copy link
Copy Markdown
Contributor Author

@Niko1221 I cannot reopen the PR, let me know if I need to open a new one. Thank you!

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.

3 participants