feat(sdk)!: generalize llama_cpp speculative decoding to all types#1195
feat(sdk)!: generalize llama_cpp speculative decoding to all types#1195Mengsheng Wu (mengshengwu) wants to merge 5 commits into
Conversation
cf697fb to
88428ed
Compare
Test evidenceSnapdragon X Elite, Windows on ARM64. Build: Models
Prompt for every run: CommandsSet once per shell: Baseline (no spec): Ngram-mod (no draft model): Draft-mtp (needs the paired drafter): Results (verbatim
|
| device | run | [ok] line |
|---|---|---|
| CPU | baseline | ngl=0 ttft=11.8ms prefill=1211.5tps decode=116.5tps gen=20 tok |
| CPU | ngram-mod | ngl=0 ttft=22.7ms prefill=625.1tps decode=115.1tps gen=30 tok |
| GPU (Adreno/OpenCL) | baseline | ngl=-1 ttft=72.8ms prefill=192.8tps decode=73.2tps gen=20 tok |
| GPU | ngram-mod | ngl=-1 ttft=75.3ms prefill=186.9tps decode=72.4tps gen=30 tok |
| HTP0 | baseline | ngl=-1 ttft=104.5ms prefill=134.2tps decode=42.1tps gen=20 tok |
| HTP0 | ngram-mod | ngl=-1 ttft=107.7ms prefill=130.5tps decode=42.1tps gen=30 tok |
Draft-mtp, Gemma-4-12B-Q4_K_M + paired drafter, GPU:
[ok ] cell plugin=llama_cpp device=gpu(id=GPUOpenCL) ngl=-1 ttft=693.3ms prefill=20.2tps decode=1.7tps gen=30 tok
[spec] draft acceptance = 0.06667 (5 accepted / 75 generated)
What the numbers say
- No regression: baseline decode on all three devices matches upstream
llama-clibuilt from the same b9775 (local check: ~72–75 t/s GPU on this model). - ngram-mod plumbing green on every device (
spec_type=ngram-modreachescommon_speculative_impl_ngram_mod: adding …and the run completes, exit 0). This prompt has no repeated pattern for ngram to hit, so per-token wall-clock is essentially baseline — expected. The value ofngram-*is on code / summary / repetitive text, not one-shot Q&A. - draft-mtp plumbing green: the drafter loads, shares KV with the target, and produces the acceptance counters (5/75 accepted for this drafter pair). Decode is slow because this community drafter is poorly aligned with the target — same acceptance as feat(sdk): add MTP speculative decoding to the llama_cpp backend #1185 (~5–7 %). Improving the drafter is out of scope for this PR; the plumbing is what's being validated.
Negative paths
--spec-type draft-mtpwithout--draft-model→ clean error, no crash, andsetup_speculativelogs--spec-type 'draft-mtp' requires a draft model (--draft-model).- Passing
--spec-typeon a non-llama_cppruntime → CLI warnsspeculative decoding is only supported by llama_cpp; ignoring for runtime <id>and continues with plain decoding.
88428ed to
337dca3
Compare
The hexagon backend rework since our previous pin (b9775) is now stable on Windows-ARM64 and materially faster on HTP0 — verified end-to-end on Snapdragon X Elite (see PR evidence comment): HTP decode 42 -> 56 tok/s (+34%), prefill 134 -> 372 tok/s. GPU / CPU baselines unchanged. draft-mtp and ngram-mod paths remain green. Companion tweaks: - Refresh sdk/patches/llama-hexagon-release-sessions.patch anchor line numbers against the new tip; content unchanged. - sdk/plugins/llama_cpp/src/vlm.cpp: init the new mtmd_input_text.text_len field (upstream added it; leaving it uninitialised silently corrupts VLM tokenisation). Signed-off-by: Mengsheng Wu <mengshen@qti.qualcomm.com>
Replace the MTP-only spec_draft_model/spec_n_draft fields on geniex_ModelConfig with a generalized speculative config: spec_type (comma-separated llama.cpp type names) + spec_draft_model + spec_n_max/spec_n_min/spec_p_min. The plugin's setup_speculative now parses spec_type via common_speculative_types_from_names and covers all nine types on b9775 — draft-model (mtp / eagle3 / draft-simple, needs a draft GGUF) and self-speculative (ngram-simple / ngram-map-k / ngram-map-k4v / ngram-mod / ngram-cache, no draft model). The draft context path keeps our device-selection pinning so a Hexagon target target does not lose the drafter to default placement. geniex-bench mirrors the flags. Signed-off-by: Mengsheng Wu <mengshen@qti.qualcomm.com>
Wire the generalized speculative fields through geniex infer. --spec-type accepts one or a comma-separated set of llama.cpp type names; --draft-model resolves either a local GGUF path or a catalogue name (auto-pulling via the model manager, matching the main-model argument's behaviour). Non-llama_cpp runtimes warn and drop the spec flags rather than erroring. Signed-off-by: Mengsheng Wu <mengshen@qti.qualcomm.com>
reacquire_before_load() calls the HTP FastRPC session-reacquire proc unconditionally, which can crash on CPU/GPU inference paths when the ADSP domain is in a bad state. Only reacquire when the target device is actually HTP (npu). cpu / gpu targets no longer depend on the ADSP domain's health. Signed-off-by: Mengsheng Wu <mengshen@qti.qualcomm.com>
…droid) Mirror geniex_ModelConfig's new spec_type / spec_draft_model / spec_n_max / spec_n_min / spec_p_min and geniex_ProfileData's draft_n_total / draft_n_accepted across the three FFI mirrors per CONTRIBUTING §4. Signed-off-by: Mengsheng Wu <mengshen@qti.qualcomm.com>
337dca3 to
9942157
Compare
Test evidence after submodule bumpSame X Elite host, same models, same commands as the previous evidence comment. Branch tip: Results (verbatim
|
| device | run | [ok] line |
|---|---|---|
| CPU | baseline | ngl=0 ttft=15.4ms prefill=925.4tps decode=80.9tps gen=20 tok |
| CPU | ngram-mod | ngl=0 ttft=27.3ms prefill=519.8tps decode=117.5tps gen=30 tok |
| GPU (Adreno/OpenCL) | baseline | ngl=-1 ttft=72.0ms prefill=195.0tps decode=75.8tps gen=20 tok |
| GPU | ngram-mod | ngl=-1 ttft=74.0ms prefill=189.9tps decode=76.1tps gen=30 tok |
| HTP0 | baseline | ngl=-1 ttft=37.9ms prefill=371.8tps decode=56.4tps gen=20 tok |
| HTP0 | ngram-mod | ngl=-1 ttft=44.2ms prefill=320.1tps decode=55.9tps gen=30 tok |
Draft-mtp, Gemma-4-12B-Q4_K_M + paired drafter, GPU:
[ok ] cell plugin=llama_cpp device=gpu(id=GPUOpenCL) ngl=-1 ttft=565.0ms prefill=24.8tps decode=1.8tps gen=30 tok
[spec] draft acceptance = 0.06667 (5 accepted / 75 generated)
Delta vs previous b9775 evidence
| device | b9775 decode | b10056 decode | Δ |
|---|---|---|---|
| CPU baseline | 80.8 | 80.9 | ~ |
| GPU baseline | 73.2 | 75.8 | +4 % |
| HTP0 baseline | 42.1 | 56.4 | +34 % |
| CPU ngram-mod | 115.1 | 117.5 | +2 % |
| GPU ngram-mod | 72.4 | 76.1 | +5 % |
| HTP0 ngram-mod | 42.1 | 55.9 | +33 % |
| GPU draft-mtp | 1.7 (acc 5/75) | 1.8 (acc 5/75) | ~ |
Prefill: HTP prefill 134 → 372 tok/s (~3×). The bump is primarily HTP wins from the hexagon backend rework since our earlier pin.
Summary
Extend the
llama_cppbackend from MTP-only (#1185) to all speculative types llama.cpp exposes upstream.CLI
--spec-type(comma-separated for chaining, llama.cpp picks in fixed priority):draft-mtp,draft-eagle3,draft-simplengram-simple,ngram-map-k,ngram-map-k4v,ngram-mod,ngram-cache--draft-modeltakes a catalogue name (auto-pulled like the main model) or a local GGUF path.Examples (verified on Snapdragon X Elite,
GENIEX_PLUGIN_QAIRT=OFF):FFI (breaking)
geniex_ModelConfig:spec_draft_model+spec_n_draft→spec_type+spec_draft_model+spec_n_max+spec_n_min+spec_p_min. Mirrored across Go, Python, Android per CONTRIBUTING §4.geniex_ProfileDatakeepsdraft_n_total/draft_n_accepted.llama.cpp submodule
Bumped to master (b10056-38,
6d5a910c5). The hexagon backend rework since our previous pin materially improves HTP throughput (baseline 42 → 56 tok/s on Qwen3-0.6B, +34 %). Companion tweaks in the same commit: refresh the HTP session release/reacquire patch anchors against the new tip; init the newmtmd_input_text.text_lenfield upstream added.Supersedes #1185.
Test plan
Snapdragon X Elite,
GENIEX_PLUGIN_QAIRT=OFF, Qwen3-0.6B-Q8_0 + Gemma-4-12B-MTP.--spec-type) — decode 80.9 / 75.8 / 56.4 tok/s--spec-type ngram-mod— no draft model, no crashes, ngram counters populate--spec-type draft-mtp --draft-model <drafter>— draft acceptance reported (5/75 for this drafter pair; matches feat(sdk): add MTP speculative decoding to the llama_cpp backend #1185)--draft-modelerrors cleanly, non-llama_cpp runtime warns and drops the flags