@@ -648,3 +648,68 @@ pattern), probe at scripts/probe_speculative.py. Runtime:
648648interscript-ts SpeculativeModel (PR #77 ). The lite tier stays the
649649standalone fast path; this is a middle tier — 2.1 outputs at a
650650fraction of the decode calls.
651+
652+ ## Speculative wall-clock on CPU: the first implementation is SLOWER (2026-09-12)
653+
654+ Follow-up measurement to the acceptance entry (bench:
655+ interscript-ts/scripts/bench-speculative.mts, 5 golden rows, warm,
656+ arm64 CPU / ORT-native):
657+
658+ | path | total s | tokens/s |
659+ | ---| ---| ---|
660+ | 2.1 int8 plain (KV greedy) | 19.8 | 67 |
661+ | lite int4 plain (KV greedy) | 61.5 | 22 |
662+ | 2.1 via speculative | 113.8 | 12 |
663+
664+ Acceptance held at the bench (0.9880, matching the probe's 0.9886) —
665+ the acceptance math is sound. The wall-clock is not, for two measured
666+ reasons:
667+
668+ 1 . ** Both speculative paths rebuild KV from zero every block** — the
669+ drafter re-prefills [ PAD] +prefix per block and the verifier runs the
670+ full sequence with zero pasts per review: O(T^2) against the plain
671+ path's O(T). This is an implementation defect, not a property of
672+ the method; the production design carries pasts across blocks.
673+ 2 . ** int4 decode is slower than int8 on this CPU** (61.5s vs 19.8s for
674+ the plain paths): ORT's CPU int4 path (MatMulNBits decompression)
675+ loses to the int8 kernels on arm64 — the drafter is the expensive
676+ model here, inverting the small-drafter premise on this hardware.
677+
678+ Consequence for positioning: the tier's published claims ("~ 9x fewer
679+ verifier invocations", output-preserving) stand; no wall-clock speedup
680+ is claimed anywhere, and none should be until the KV-carrying
681+ implementation is measured. On hardware with fast int4 (or GPU
682+ verifier batches) the cost model inverts in the tier's favor.
683+
684+ ## Decode framing is a third parity axis on dynamic-int8 artifacts (2026-09-12)
685+
686+ The KV-carrying rewrite of the runtime's speculative decode exposed a
687+ mechanism the cross-hardware study (2026-09-07) did not cover:
688+
689+ ** Dynamic-int8 ONNX graphs compute activation quantization scales per
690+ fed tensor.** Decode framing — how many tokens share one decoder call
691+ — therefore changes the numerics materially, on the SAME machine, same
692+ runtime, same artifact:
693+
694+ - batched feed (K tokens with pasts) vs single-step feed: present
695+ values differ up to ~ 0.03 per element (fp32 KV would be ~ 1e-6);
696+ inner-position argmax flips are routine
697+ - the batched-framing greedy is a DIFFERENT DECODE than the
698+ single-step one: drafter==verifier self-acceptance measured 33/66
699+ despite identical final strings via self-correction; the
700+ int4->int8 pair's batched-verifier output lost a word
701+ ("امُ عليكم" vs "السلام عليكم")
702+ - measured pair acceptance under runtime framing (single-step
703+ drafting vs batched verification): ** 0.4614** , vs the probe's
704+ 0.9886 measured under uniform plain-path framing — the probe
705+ number is framing-relative, and the runtime number is the real one
706+
707+ Wall-clock (arm64 CPU, warm, 5 golden rows): plain 2.1 int8 21.4s;
708+ lite int4 75.7s (int4 CPU kernels lose to int8 — MatMulNBits); the
709+ O(T) speculative pair 155.4s at 0.46 acceptance. Verdict: ** the
710+ speculative tier is not viable on quantized CPU artifacts** — the
711+ int4 drafter is the expensive model AND framing divergence collapses
712+ acceptance. The technique's domain is fp-class artifacts or serving
713+ paths with consistent framing. The playground tier was pulled
714+ accordingly; the runtime keeps SpeculativeModel as measurement
715+ infrastructure with the constraint documented.
0 commit comments