Skip to content

Commit 437102e

Browse files
authored
KVFlash: bounded KV residency (lookahead sparse attention) for dflash (#373)
Fixed-pool resident KV with 64-token chunk paging to host RAM (bit-exact), drafter-scored residency (default; LRU fallback), --kvflash auto VRAM sizing. Spark MoE spec decode fixed (cublas-free shexp gate + DeltaNet snapshot). Verified on RTX 3090: test_kvflash A-F all pass, harness 36/36, drafter 15/16 vs LRU 0/16 NIAH, 99.2% KV cut.
1 parent 42619b0 commit 437102e

41 files changed

Lines changed: 4432 additions & 205 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ jobs:
128128
needs: [uv-workspace]
129129
runs-on: [self-hosted, gpu, sm86]
130130
timeout-minutes: 30
131-
# The box has a single physical GPU: serialize GPU jobs across PRs instead
132-
# of letting concurrent runs clobber each other.
131+
# Serialize CUDA jobs across PRs (one RTX 3090). The ROCm job has its
132+
# own group: different physical GPU, no contention.
133133
concurrency:
134134
group: lucebox3-gpu-runner
135135
cancel-in-progress: false
@@ -197,15 +197,51 @@ jobs:
197197
needs: [uv-workspace]
198198
runs-on: [self-hosted, rocm, gfx1151]
199199
timeout-minutes: 20
200-
# Same single box as gpu-tests: serialize GPU jobs across PRs.
200+
# Serialize across PRs per GPU. NOT the same group as the CUDA job:
201+
# the combo box has two distinct GPUs (RTX 3090 + Strix iGPU), and a
202+
# shared group only holds one waiting job, so the Radeon leg was
203+
# chronically displaced ("higher priority waiting request") by every
204+
# new CUDA job entering the queue.
201205
concurrency:
202-
group: lucebox3-gpu-runner
206+
group: lucebox3-rocm-runner
203207
cancel-in-progress: false
204208
steps:
205209
- uses: actions/checkout@v4
206210

211+
- name: KFD health (diagnose instead of hanging)
212+
# rocminfo on a wedged KFD blocks in uninterruptible sleep and eats
213+
# the whole 20-minute job timeout. Probe with a hard timeout first,
214+
# and when it hangs, dump the evidence (D-state holders, dmesg) so
215+
# the job fails in seconds with a diagnosis instead of silently.
216+
run: |
217+
# A wedged KFD puts rocminfo in UNINTERRUPTIBLE sleep: timeout(1)
218+
# cannot kill it and a foreground wait blocks until the job
219+
# timeout. Probe in the background (output to a file so no pipe
220+
# keeps the step alive) and enforce the deadline in the shell.
221+
/opt/rocm/bin/rocminfo > /tmp/rocminfo.out 2>&1 &
222+
PROBE=$!
223+
for i in $(seq 1 15); do
224+
kill -0 $PROBE 2>/dev/null || break
225+
sleep 1
226+
done
227+
if kill -0 $PROBE 2>/dev/null; then
228+
echo "::error::rocminfo hung (likely D-state) — ROCm/KFD wedged; the box needs a reboot"
229+
echo "--- probe state:"
230+
ps -o pid,stat,wchan:32,comm -p $PROBE || true
231+
echo "--- processes holding /dev/kfd:"
232+
sudo fuser -v /dev/kfd 2>&1 || true
233+
echo "--- D-state processes:"
234+
ps -eo pid,user,stat,wchan:32,comm | awk '$3 ~ /D/' || true
235+
echo "--- recent amdgpu/kfd dmesg:"
236+
sudo dmesg 2>/dev/null | grep -iE "amdgpu|kfd" | tail -15 || true
237+
kill -9 $PROBE 2>/dev/null || true
238+
disown $PROBE 2>/dev/null || true
239+
exit 1
240+
fi
241+
wait $PROBE && echo "KFD healthy" || { echo "::error::rocminfo exited non-zero"; cat /tmp/rocminfo.out | tail -5; exit 1; }
242+
207243
- name: ROCm smoke (rocminfo sees gfx1151)
208-
run: /opt/rocm/bin/rocminfo | grep -E "Name:|Marketing Name:" | grep -iE "gfx1151|Radeon 8060S"
244+
run: cat /tmp/rocminfo.out | grep -E "Name:|Marketing Name:" | grep -iE "gfx1151|Radeon 8060S"
209245

210246
- name: Build + run HIP vector-add on the Radeon 8060S
211247
# Self-contained HIP kernel correctness test (no model weights). This is

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ Each one is self-contained with setup instructions and benchmark notes.
4040
<a href="optimizations/spark/"><img src="assets/cards/spark_card.png" alt="Luce Spark MoE expert offload" width="46%"></a>
4141
</p>
4242

43+
<p align="center">
44+
<a href="optimizations/kvflash/"><img src="assets/cards/kvflash_card.png" alt="Luce KVFlash paged KV cache" width="46%"></a>
45+
</p>
46+
4347
---
4448

4549
## Supported Models & Drafters
@@ -280,6 +284,18 @@ When compression is on, the request path picks one of three modes automatically,
280284
| `--kv-cache-dir <path>` || Persist prefix cache to disk |
281285
| `--kv-cache-budget N` || On-disk cache size cap |
282286

287+
**Bounded KV residency (KVFlash)**
288+
289+
Pages the attention KV cache through a fixed pool of GPU slots; cold 64-token chunks live in host RAM, bit-exact and recallable. Decode speed stops depending on context length and resident KV stays pool-sized at any context. Off by default; works on every model family. Drafter-scored residency is the default on every family: the server finds the Qwen3-0.6B drafter next to the model (or via `--prefill-drafter`) and lazy-loads it as the relevance scorer that decides which chunks stay resident — non-qwen targets (laguna, gemma4) bridge the tokenizer gap by re-tokenizing the context text for the drafter. LRU is the fallback when no drafter is present, or the explicit choice via `--kvflash-policy lru`. Per-model numbers in [Luce KVFlash →](optimizations/kvflash/README.md).
290+
291+
| Flag / env | Default | Effect |
292+
|---|---|---|
293+
| `--kvflash <tokens\|auto>` | off | Resident pool size. `auto` sizes from the GPU: half of free VRAM after weights and reserves, at the model's KV density, capped where decode speed stays near the flat optimum (default 16384, override `DFLASH_KVFLASH_MAX_POOL`) and at `--max-ctx`. Explicit values are rounded to 256, clamped to `--max-ctx`, floored at the protected minimum so eviction always has a victim. |
294+
| `--kvflash-policy {drafter,lru}` | `drafter` | Residency policy. `lru` opts out of the drafter probe/load (recency-only paging, no extra VRAM). |
295+
| `--kvflash-tau N` | `64` | Reselect interval floor (drafter policy only); the effective interval grows with history to cap rescore overhead. |
296+
| `DFLASH_KVFLASH=N` | off | Env equivalent of `--kvflash`. |
297+
| `DFLASH_KVFLASH_TAU=N` | `64` | Env equivalent of `--kvflash-tau`. |
298+
283299
**Thinking budget**
284300

285301
| Flag | Default | Effect |

0 commit comments

Comments
 (0)