107 lines
6.1 KiB
Markdown
107 lines
6.1 KiB
Markdown
# Roadmap
|
||||
|
|
|
|||
|
|
Ordered by whether the verification method is *correct*, not by expected size.
|
|||
|
|
That ordering is deliberate: this project retracted five conclusions, and in
|
|||
|
|
every case the fix was to the measurement rather than the code.
|
|||
|
|
|
|||
|
|
Each item states the measurement it starts from, what it should gain, and how
|
|||
|
|
that gain would be verified. An item without a verification method is not on
|
|||
|
|
this list.
|
|||
|
|
|
|||
|
|
## Done
|
|||
|
|
|
|||
|
|
| | item | result |
|
|||
|
|
|---|---|---|
|
|||
|
|
| H1 | Task oracles judged by device state, with variants | alarm oracle needed a *saved* alarm, not a typed `7`; a Wi-Fi round trip now fails the already-satisfied task |
|
|||
|
|
| H2 | Harness semantics | arm order really rotates; `best` respects metric direction; provenance stamped; failures recorded |
|
|||
|
|
| H3 | The three missing measurements | `ready_s`, peak `RssAnon`, memory at a filled context — all previously unmeasurable |
|
|||
|
|
| H4 | Contention guard | aggregate stolen capacity + straggler rule, on the target cores rather than host load average |
|
|||
|
|
| H5 | Regression gates | bands from observed spread; throughput explicitly ungateable at ~30% swing |
|
|||
|
|
| H6 | Fidelity metric that ranks | top-1 agreement and KL, replacing a saturating 12-prompt greedy check |
|
|||
|
|
| H8 | Retracted numbers purged from source | two modules were still shipping withdrawn figures as documentation |
|
|||
|
|
| I0 | Blocked weight repack | **489 MB** of load-time peak, no cost |
|
|||
|
|
| I1 | One packed weight layout | **615 MB** resident, at ~1.63× prefill |
|
|||
|
|
| I2 | Measure `cpu_gemm_wna16` | 2.2× slower than tinygemm; routing was right, now for a measured reason |
|
|||
|
|
| I7 | Memory ledger, by attribution | found a **618 MB** RoPE table and an **843 MB** KV cache, both inside an "unexplained 1.6 GB" |
|
|||
|
|
|
|||
|
|
## Next
|
|||
|
|
|
|||
|
|
**I6 — import cost.** `import vllm_omni` is ~30 s against `import vllm`'s 5.2 s,
|
|||
|
|
with transformers' auto image-processing (4.6 s) and the `openai` client (1.3 s)
|
|||
|
|
identifiable inside it, for a text model that needs neither. Every stage
|
|||
|
|
subprocess pays it again.
|
|||
|
|
*Expect:* an upper bound of ~6 s per process; deferred imports often move cost
|
|||
|
|
rather than remove it. *Verify:* `ready_s`, which now exists — the old timer
|
|||
|
|
started after imports, so the benchmark meant to prove this could not see it.
|
|||
|
|
|
|||
|
|
**Drop the row-major embedding copy.** `embed_tokens.weight_packed` is 128 MB,
|
|||
|
|
kept solely because the tinygemm layout was believed un-addressable for a
|
|||
|
|
single-row lookup. That premise is now known false. *Verify:* `RssAnon` plus an
|
|||
|
|
embedding-output SNR assertion.
|
|||
|
|
|
|||
|
|
**I10 first step — why is the GEMM at 60%?** The decode GEMM sustains 132 GB/s
|
|||
|
|
against a measured 222 GB/s ceiling. Part is arithmetic (tinygemm dequantizes
|
|||
|
|
per activation row) and part may be that each of 113 calls streams only a few MB,
|
|||
|
|
too little for the prefetcher to reach steady state. Attribute before optimising.
|
|||
|
|
*Expect:* unknown — a diagnosis, whose output is whether the 2.76 ms implied by
|
|||
|
|
the gap is reachable at all.
|
|||
|
|
|
|||
|
|
**I3 — asymmetric W4A8.** The symmetric grid is our exporter's choice, not the
|
|||
|
|
kernel's limit; `models/spark2_5-gptq-asym-g128` already exists. The cheapest
|
|||
|
|
untried fidelity experiment, and it tests the *stated* cause of W4A8's error.
|
|||
|
|
*Verify:* `fidelity_kl.py` and the scored agent suite.
|
|||
|
|
|
|||
|
|
**I5 — attention wrapper consolidation.** Four ops per layer, 1.31 ms/step, of
|
|||
|
|
which 0.83 ms is wrapper self time; at batch 1 the cache-update loop runs two
|
|||
|
|
iterations while the wrapper rebuilds context and allocates a dummy tensor for
|
|||
|
|
compile ordering. *Expect:* 0.3–0.5 ms, revised down from an earlier 0.6–0.9 ms
|
|||
|
|
that assumed a workload never profiled. *Verify:* per-op profile **and**
|
|||
|
|
unprofiled step latency — one outer call that still launches two threaded
|
|||
|
|
kernels removes neither thread launch.
|
|||
|
|
|
|||
|
|
**I4 — a better quantizer.** Ours reproduces 2/12 greedy prompts against its own
|
|||
|
|
bf16; Q4_K_M manages 4/12 — but at 4.5 bits against our 4.25, so close the
|
|||
|
|
budget gap first (g64, or fp16 scale+zero, both config changes) before
|
|||
|
|
implementing error-compensated rounding. Timebox it; a null result is worth
|
|||
|
|
recording.
|
|||
|
|
|
|||
|
|
## Considered and rejected
|
|||
|
|
|
|||
|
|
**A CPU megakernel.** On GPU this fuses a forward pass into one launch to remove
|
|||
|
|
launch overhead and the cross-kernel prefetch bubble. On CPU the analogues are
|
|||
|
|
OpenMP fork/join and ATen dispatch — and llama.cpp already is one:
|
|||
|
|
`ggml_threadpool` starts threads once and they walk the whole graph,
|
|||
|
|
synchronising at `ggml_barrier` through an atomic spin, where vLLM enters a
|
|||
|
|
separate `omp parallel` region per kernel.
|
|||
|
|
|
|||
|
|
Measured before deciding: a parallel region costs ~1.2 µs at 24 threads, and
|
|||
|
|
spin-waiting does not help (`OMP_WAIT_POLICY=ACTIVE` moved it 1.23 → 1.18 µs), so
|
|||
|
|
it is barrier cost rather than thread wake-up. At a few hundred regions per
|
|||
|
|
decode step that is 0.3–0.4 ms, 2–3% — the same money as I5, far more cheaply.
|
|||
|
|
The larger prize is the streaming bubble, but capturing it means replacing the
|
|||
|
|
execution model inside vLLM's CPU backend, which reimplements ggml. Not next.
|
|||
|
|
|
|||
|
|
**More kernel fusion.** Four attempts, four losses: vLLM's fused CPU RMSNorm is
|
|||
|
|
3.6–4.5× faster in isolation and slower in situ (inductor already fuses the
|
|||
|
|
native form with the residual add); fused `gelu_and_mul` 2.7× slower at Spark's
|
|||
|
|
shape; `cpp.min_chunk_size` −6% decode; an inline gate reduction −44%.
|
|||
|
|
|
|||
|
|
**MiniCPM-o CPU overlay.** The 8 B thinker at bf16 is ~19 GB, so it buys no edge
|
|||
|
|
deployability. The argument for it — that three-stage parallel init should beat
|
|||
|
|
the two-stage 2.1× — is also not how overlap works: the bound is ΣT/max(T).
|
|||
|
|
|
|||
|
|
**Speculative decoding for tg128.** It would inflate a fixed-prompt `ignore_eos`
|
|||
|
|
benchmark without representing anything. Measure it on the agent suite or not at
|
|||
|
|
all.
|
|||
|
|
|
|||
|
|
**Thread count and warm-up batch size as memory levers.** 64 MB and 2 MB.
|
|||
|
|
|
|||
|
|
## Harness work still open
|
|||
|
|
|
|||
|
|
- **A real device in the agent loop.** The toolkit has 38 tests and has never
|
|||
|
|
touched a phone — this host has no `adb`, no emulator, no `/dev/kvm`. The
|
|||
|
|
real-device path and per-task resets exist; what is untested is whether a real
|
|||
|
|
Settings app matches the fixtures.
|
|||
|
|
- **Success rates, not single runs.** The build comparison that produced
|
|||
|
|
"navigation survives quantization, completion does not" is one run per cell.
|
|||
|
|
Variants now exist so repeats are trials; they need running.
|