Skip to content

pool: ablate to what kern-serve uses; one GPU e2e gate over the four spec families - #18

Merged
xiaguan merged 6 commits into
pool-radixfrom
ablate
Sep 15, 2026
Merged

xiaguan merged 6 commits into
pool-radixfrom
ablate

Conversation

@xiaguan

@xiaguan xiaguan commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Stacked on #17 (pool-radix); base moves to master once that merges.

Ablation, judged from kern-serve

kern-pool keeps what its one consumer uses. Prefix makes room itself (evict(park)Evicted::Parked / Dropped), coldest / park / remove are no longer public, Store exposes tokens / has_slot only, Host is built from bytes, Waking::tokens and Tray::fork are gone. The crate has no mod tests; its accounting is tested through what it hands out (tests/pool.rs reference Mapping model landing Remap plans, tests/prefix.rs brute-force model learning tiers from Evicted, tests/host.rs).

One GPU e2e over the four spec families

tools/e2e/e2e.py drives every target of a kern.toml through kern-serve the way a client does (README in the dir): kern test, conc1 vs kern run, repeat / turn2 hits, warm vs kern run and vs a cold server, twelve at once, a client hanging up mid-stream, a small pool with a host tier filled until the server parks, slot growth from two slots, --rows 1. Byte identity gates; a divergence on a single-rank target is excused only by the logits of that step (kern run --prompt-ids <shared context> --rows 1 --probe-dir, both tokens within 4 ULPs of the top); tray targets report cross-path identity instead of gating it.

Gate 2026-09-14 (tray06 / tray07 / tray09, 4×GB300 each): qwen3-4b, qwen3-4b-dspark, qwen3.8-27b, qwen3.8-27b-dflash2, DeepSeek-V4.1-Flash EP4, Kimi-K3 4-layer EP4 — all pass. Table in docs/serve.md "e2e 门禁".

Writing the scenarios found three interface problems, fixed here:

  • kern run --prompt-ids: an answer's text need not tokenize back to its ids, so turn2 is sent as ids and the oracle replays them.
  • kern-serve: a speculative round accepting past max_tokens or the stop left a request-end snapshot keyed by tokens no next turn sends (DSv4.1 2/4, dflash2 1/4 turn2 misses, a slot held until evicted). Not kept any more (not kept debug line); a state ending exactly at the stop token is.
  • kern-serve: a scheduler panic left the port open with every request hanging; the process exits 101.

Open, noted in serve.md: K3 4-layer at the default budget is 131k chunks of 2 MiB (chunk = half the smallest object), 40–60 s of mapping per rank and one cuMemSetAccess OOM — the chunk should scale with the budget.

Checks

host: cargo test --release, cargo clippy --all-targets -D warnings, fmt, hawk; kernel-lab: cargo clippy -p kern-serve, cargo test -p kern-serve; CI's manifest-name grep.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G6hPjvcgRZcdFcymLwFchB

xiaguan and others added 6 commits September 14, 2026 11:44
…hat it hands out

Trimmed against the one consumer, kern-serve: the make-room loop the
scheduler and agentx_replay each carried (coldest resident → park, drop
the coldest parked until it fits, else drop) is `Prefix::evict`, which
names the entry it took (`Evicted::Parked(key)` / `Dropped { key,
parked }`); `coldest`, `park` and `remove` are private, `Hit::tier /
resident / parked`, `Prefix::resident / parked / is_empty`, `Store::pages
/ paged`, `Host::bytes / pages / page_bytes / slot_bytes`,
`Parked::slot_offset`, `Remap::is_empty`, `Waking::tokens` and
`Tray::fork` had no caller outside tests and are gone; `Pooled::objects`
is private.

The three `mod tests` that read the ledger (chunks, the pool's statuses,
the tree's edges) are gone with the `cfg(test)` accessors they needed.
What they checked is checked one level up, through the public API: the
pool property tests land every planned `Remap` into a model of the
chunks (a chunk is free or at one position, whatever a handle names is
mapped whole, the counters say what the plans said), the prefix model
learns what `evict` took from the key it names, and `runs` is an
integration test. kern-pool: 40 tests, no GPU.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G6hPjvcgRZcdFcymLwFchB
Signed-off-by: JinYan Su <751080330@qq.com>
The e2e driver compares a served answer with kern run's on the same
tokens; a prompt given as text tokenizes the same on both sides only
until an answer's text is fed back (it need not tokenize to its ids).
Given ids, the oracle runs exactly what the server ran.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G6hPjvcgRZcdFcymLwFchB
Signed-off-by: JinYan Su <751080330@qq.com>
A speculative round accepts tokens the client never gets: past
max_tokens, or past the stop token. With a recurrent state they are in
the sequence's state, and the request-end snapshot keyed by them is a
prefix no next turn sends; it held a slot until evicted and never hit
(e2e turn2 on DSv4.1: 2 of 4 misses, dflash2: 1 of 4). Such a state now
returns to the pool at once, with a debug line; a state ending exactly
at the stop token is kept, a chat's next turn ends the answer with it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G6hPjvcgRZcdFcymLwFchB
Signed-off-by: JinYan Su <751080330@qq.com>
The scheduler thread unwinding left the port open and every request
hanging until the client's timeout; the e2e driver sat 15 minutes on
one. The process exits 101 instead, the panic message already printed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G6hPjvcgRZcdFcymLwFchB
Signed-off-by: JinYan Su <751080330@qq.com>
One driver, one kern.toml, every target through the same scenarios over
the binaries a user has: kern test, conc1 against kern run, the hits
the checkpoints allow (repeat, turn2 as ids), warm against kern run and
against a cold server, twelve at once, a client hanging up mid-stream,
a small pool with a host tier filled until the server parks, a
stateful manifest growing its slots from two, a speculative manifest
at --rows 1. Byte identity gates; a divergence on a single-rank target
is excused only by the logits of that very step (kern run --prompt-ids
over the shared context, both tokens within 4 ULPs of the top), and a
tray target reports what crossed numerical paths instead of gating it.

Gate on 2026-09-14 (tray06 / tray07 / tray09): qwen3-4b, its dspark
draft, qwen3.8-27b, its dflash2 draft, DeepSeek-V4.1-Flash EP4 and
Kimi-K3 4 layers EP4 all pass; numbers in docs/serve.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G6hPjvcgRZcdFcymLwFchB
Signed-off-by: JinYan Su <751080330@qq.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G6hPjvcgRZcdFcymLwFchB
Signed-off-by: JinYan Su <751080330@qq.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 14, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-14T13:08:02.908848Z 717169b PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 717169b7c0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tools/e2e/e2e.py
Comment on lines +654 to +655
if a.targets:
targets = [t for t in targets if t.name in a.targets]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject unknown requested targets before running the gate

When --targets contains a misspelled or renamed target, this filter silently drops it; if none of the names match, reports remains empty and the script writes an empty summary and exits successfully. A CI invocation intended to validate specific models can therefore pass without running any checks, so validate every requested name against the configured targets and fail on unknown names.

Useful? React with 👍 / 👎.

Comment on lines +466 to +467
if self.park(&key, &mut park)? {
return Ok(Some(Evicted::Parked(key)));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Report host entries dropped before a successful park

When the host is full, this loop can remove one or more parked entries and then successfully park the resident entry, but the Evicted::Parked result discards dropped. Both KernScheduler::make_room and agentx_replay::make_room only add to host_evictions from the Dropped variant, so this common successful-retry path underreports host evictions and corrupts the new gate's reported counters.

Useful? React with 👍 / 👎.

Comment on lines +738 to +740
let visible = s.prompt_len + s.emitted;
let past = s.history.get(visible..).unwrap_or_default();
if s.pos > visible + 1 || past.iter().any(|&t| !self.policy.stop_tokens.contains(&(t as u32))) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep extra stop tokens only after stop termination

For a speculative request that reaches max_tokens before a later accepted draft, this condition treats one unseen draft as valid whenever its ID happens to be a configured stop token, even though emit finished the request with Length (and likewise when EOS handling is ignored). The resulting state is indexed with a token the client never received and cannot include in its next prompt, leaving a useless recurrent-state slot occupied until eviction; the exception needs to depend on the actual finish reason rather than only the token value.

Useful? React with 👍 / 👎.

Comment thread tools/e2e/e2e.py
Comment on lines +347 to +349
cmd = self.command(context) + ["--steps", "2", "--probe-dir", str(d), "--probe-steps", "2", "--probe-labels", ""]
if rows > 1:
cmd += ["--rows", "1"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Force one-row mode for every near-tie probe

When session_rows1 encounters a divergence, it calls this method with rows == 1, so the probe omits --rows 1; on the speculative targets for which that session runs, kern run then defaults to the widest declared row shape (crates/kern-run/src/run.rs:321). The near-tie verdict is consequently based on a different speculative path rather than the plain path being compared, which can incorrectly excuse or reject a rows-1 regression.

Useful? React with 👍 / 👎.

Comment thread tools/e2e/e2e.py
Comment on lines +389 to +390
key = json.dumps([prompt, steps, rows])
if key not in self.cache:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Key cached oracle answers by the tested artifacts

The persistent oracle.json key contains only the prompt, step count, and row count. Reusing an output directory after changing the manifest, kernels, weights, or kern binary therefore skips the current oracle run and compares the new server against an answer produced by an older artifact set, making the gate result invalid and commonly producing false failures. Include artifact/binary identity in the cache key or clear the cache for each invocation.

Useful? React with 👍 / 👎.

@xiaguan
xiaguan merged commit 717169b into pool-radix Sep 15, 2026
3 checks passed
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