frank-large: double the KV pool to 524288 positions

HALOGEN_KV_POOL_POSITIONS 262144 -> 524288, which is upstream's own
default (2 x HALOGEN_CTX). The old pool was the binding constraint on
concurrency, not context: a request reserves prompt + max_tokens against
the pool, so with pool == CTX four concurrent 65k chats were contending
for one full-length conversation's worth of positions. Two full-length
conversations, or four at 131k, now stay resident.

Cost is ~7.2 GiB more device RAM (measured 28 KiB/position), taken from
the page cache the 47.7 GiB n-gram lookup table reads through. Live
startup ledger for the prior config: 68.0 weights + 7.2 pool + 21.1
working = 96.3 GiB held, 12.1 GiB left on the host. The doubled pool
puts the remainder near 5 GiB against a ~4.3 GiB co-tenant working set,
and cache at ~69 GiB -- still above the lookup table, so the table can
stay hot. Compensating lever if long prompts start crawling:
HALOGEN_MAX_TOK=16384, which frees 8.3 GiB of prefill arena for ~8%
of prefill throughput.

Docs updated to match, including the admission-trap paragraph, which was
only true while pool == CTX.
This commit is contained in:
Frank
2026-09-12 17:51:24 -07:00
parent dd44223f3f
commit 5ace29e7a0
3 changed files with 53 additions and 19 deletions
@@ -11,7 +11,7 @@ tags:
- gfx1151
- qwen4exp
- rocm
timestamp: 2026-09-11
timestamp: 2026-09-12
---
# halogen-flash-server on Dankclaw (frank-large)
@@ -78,7 +78,7 @@ upstream's defaults:
|---|---|---|
| `HALOGEN_MODEL_ID` | `frank-large` | fleet identity; no client rename |
| `HALOGEN_CTX` | `262144` | the model's native context, and what both clients declare |
| `HALOGEN_KV_POOL_POSITIONS` | `262144` | conservative for a **shared** 121 GiB host: one full-length conversation, or four at 65k, at ~7.2 GiB |
| `HALOGEN_KV_POOL_POSITIONS` | `524288` | **doubled 2026-09-12** to upstream's own default (2 × `HALOGEN_CTX`): two full-length conversations resident, or four at 131k, at ~14.4 GiB |
| `HALOGEN_KV_SLOTS` | `4` | four concurrent generations; slots share the pool and cost ~115 MiB each |
| `HALOGEN_HOST_RESERVE_GIB` | `24` | RAM kept for the n-gram table the engine reads through the page cache — above upstream's 20 because a dozen containers share this host |
| `HALOGEN_VISION_TOWER` | `1` | image input; the fleet's only image path since [[frank-npu]] was retired |
@@ -103,7 +103,8 @@ Three numbers explain the whole box:
file cache, so `free`, `MemAvailable` and every dashboard built on them
**overstate free memory by the size of the model**. The server prints the
corrected figure at startup; believe that line, not Grafana.
- **~7.2 GiB per 262,144 KV positions**, plus the prefill arena (16.7 GiB at
- **~28 KiB per KV position** (measured: 262,144 positions = 6,656 MiB of KV,
~7.2 GiB with block scratch), plus the prefill arena (16.7 GiB at
`HALOGEN_MAX_TOK=32768`, 8.4 GiB at 16384). The engine's device-side budget
stops at roughly 47 GiB on a 128 GB machine *with the weights pinned* — and
lower on a machine carrying more besides this server, which Dankclaw is.
@@ -116,6 +117,23 @@ Three numbers explain the whole box:
the pool itself if the configured one will not fit, printing what it chose.
Read those lines on every restart; they are the ground truth for sizing.
**Measured ledger (prior 262,144 config, read live 2026-09-12).** The
engine's own startup account: 68.0 GiB of weights locked + 7.2 GiB KV pool +
21.1 GiB working memory = **96.3 GiB held**, with **12.1 GiB left on the
host**, only ~7.3 GiB of that in contiguous 2 MiB pieces. The 47.7 GiB n-gram
lookup table is never held in RAM — it is read through the file cache — and
the 2.0 GiB firmware carves for the iGPU appears in no `/proc/meminfo`
figure.
Doubling to 524,288 puts the held total near **103.5 GiB** and the host
remainder at roughly **5 GiB**, against a measured ~4.3 GiB working set for
the 25 co-tenant containers on this box. The extra 7.2 GiB comes out of the
page cache, which falls from ~76 GiB to ~69 GiB — still above the 47.7 GiB
lookup table, so the table can stay hot. Tight, not an overcommit. If long
prompts start crawling with the disk busy, `HALOGEN_MAX_TOK=16384` shrinks
the prefill arena 16.7 → 8.4 GiB and pays for the doubling outright at about
8 % of prefill throughput.
**Co-tenancy.** `HALOGEN_FLASH_PIN_TRUNK=1` (default) is what the published
decode numbers were measured with, and it means this container holds most of
the host. If a co-tenant then takes the remainder, both it and the engine can
@@ -156,9 +174,12 @@ deployment:
**plus** `max_tokens` positions when it is admitted; if the pool cannot hold it
the request waits in arrival order, and after the queue timeout (3600 s) it
gets a `503 engine_busy`. A prompt large enough that prompt + budget exceeds
the pool can therefore never be admitted at all. The 262,144-position pool is
the binding constraint on how large a conversation can get, not `HALOGEN_CTX`
and not the client's declared `context_length`.
the pool can therefore never be admitted at all. With the pool at 524,288 the
per-conversation ceiling is `HALOGEN_CTX` (262,144) — the pool binds **how
many** conversations stay resident, not how long any one of them can be. Under
the previous 262,144 pool the two numbers were identical, which is why four
concurrent 65k chats were contending for one conversation's worth of
positions.
## Vision