Skip to content

--spec 5 measured +6.1% decode (RTX 5060 Ti / 128 GB) but cannot be persisted: tune_knobs safe range is [3, 4] #3844

Closed

Description

@travellm

Issue draft — paste into https://github.com/Niko1221/Strata/issues/new

Title: --spec 5 measured +6.1% decode on RTX 5060 Ti / 128 GB, but it cannot be persisted: tune_knobs safe range is [3, 4]


What I am asking for

Allow --spec 5 for the flash-next family, or at least let a power user pin it durably.
I am not asking to change the default — my machine's own autotune correctly picked 4 and I
respect that. I am asking that 5 be a value a user is allowed to keep.

Environment

Engine Strata 0.1.47-windows-x86_64-cuda-cuda13 (auto-updated to 0.1.48 after the measurement — see caveats)
Model qwen3.8-flash-next-nvfp4-nvfp4 (qwen-nvfp4-gptq-dense.gguf + pack), 204800 ctx, KV int8, vision enabled
GPU RTX 5060 Ti 16 GB (16311 MiB), iGPU drives the display so the dGPU is model-only
RAM / CPU 127.5 GiB / Core Ultra 7 265K
Launch --serve, resource profile performance, --vram-reserve-mib 2048, expert cache 2226 slots (5.73 GiB), expert arena 63.28 GiB, batch 1

Measurement

Same engine build, same model, same machine, same prompts, greedy decoding, batch 1, one run per
prompt. --spec 5 was measured with a second, independent engine instance on a separate port
(18501) while the main instance ran --spec 4, then the numbers were compared. Metric is the
engine's own decode figure (generated tokens ÷ generation time, from the GEN lines), plus the
engine's own draft-acceptance counters. Client-side medians are reported too.

Run --spec engine decode tok/s draft accepted client median tok/s
baseline 4 48.8 70.9% 45.1
variant 5 51.8 (+6.1%) 67.2% 44.8

Per prompt (engine decode tok/s):

Prompt --spec 4 --spec 5
technical explanation 45.1 44.8
code generation 49.1 56.5 (+15%)
creative writing (zh) 43.2 43.4

What the guard's rationale predicts is that a wider window costs more than it pays. On this box it
did not: acceptance falls exactly as expected (70.9% → 67.2%) and the window is still net positive,
concentrated in code generation. RAM footprint is identical (the 63.28 GiB expert arena does not
depend on the window) and the VRAM cost is one small buffer:

strata verify: window up to 7 tokens, ~90 MiB of device buffers      # --spec 5
strata verify: window up to 6 tokens, 77.1 MiB of device buffers      # --spec 4

The engine starts and serves --spec 5 with no problem at all — I ran it for hours, including as
the assistant serving this very session. This is purely a policy question, not a capability one.

Why I cannot keep it (three separate walls)

  1. strata_control/tune_knobs.py — the tuner exposes "--spec": {"choices": (3, 4)}, and the
    safety text says wider windows (>= 6) were "4% to 16% slower". 5 is not rejected by that rule;
    it is rejected by the range. In-process check:
    --spec = 4   -> []                                          (passes)
    --spec = 5   -> ['--spec 5: outside the safe values [3, 4]']
    --spec = 6   -> ['--spec 6: wider speculative windows were 4% to 16% slower',
                     '--spec 6: outside the safe values [3, 4]']
    
  2. presets.overlay() — the layer documented as "applying the active preset at every start"
    deliberately drops out-of-range settings ("A setting that is outside the safe rules is left out,
    never applied"), so a preset carrying --spec 5 is skipped entirely:
    applied=False  why_not='not applied: --spec 5: outside the safe values [3, 4]'  state='fresh'
    
    With only that one range relaxed in memory (no file touched), the very same preset applies
    cleanly through the official mechanism:
    applied=True  state='fresh'  resulting argv: --spec 5
    
    So the persistence machinery already works; the range is the only thing in the way.
  3. Hand-editing the model settings file does not survive a load. strata-nvfp4-nvfp4.json
    holds "--spec", "5" → the UI's native-settings JSON shows 5 → the user presses unload/load →
    the freshly derived native-instances\<uuid>.json comes out with --spec 4 and the settings
    file is rewritten back to 4 (mtime moves to the load timestamp). This is the part I found
    surprising: the UI displays an args array that a user may edit and that validateNativeSettings()
    explicitly accepts, but the value it shows is regenerated over at load, so editing it silently
    does nothing. If that is intentional, the UI should say so; if not, it is a small bug.

Suggested fixes, in order of preference

  1. Widen the safe range for flash-next to [3, 5] (or gate it: allow 5 when vram >= 16 GB and
    ram >= 64 GB, where the window buffer is free and acceptance stays high).
  2. Add a draft-depth field to NATIVE_FIELDS (native settings → "GPU & memory" or a new
    "Speculation" group) so the value is a first-class, user-owned setting that goes through
    save() and is not regenerated at load.
  3. Independent of the above: if args edits in the model settings file are going to be regenerated
    at load, surface that in the native-settings JSON tab instead of accepting them.

Caveats I want to be upfront about

  • One run per prompt, three prompts. The aggregate +6.1% comes mostly from the code prompt; the
    other two are flat. I have not run a repeated-measures median. I would not call this a settled
    benchmark — I am reporting a directional result on one specific GPU/RAM/model/quant combination.
  • Client-side medians were flat (45.1 → 44.8), so the engine-side gain did not reach wall-clock for
    single short requests. For long generations it should.
  • Measured on 0.1.47; the engine has since auto-updated to 0.1.48. I will re-run on 0.1.48 and
    post the numbers here before this is acted on. If it does not reproduce, I will say so.
  • Autotune on this machine picked the default and reported default_confirmed with settings.args = {},
    so 5 was never a candidate it could have found. The candidate set is the reason this is invisible
    to the tuner, not a measurement disagreement.

Repro

# baseline is whatever the app launches; then start a second instance with one flag changed
strata.exe --serve --pack <pack> --native <gguf> ... --spec 5 --port 18501
# engine log: "strata verify: window up to 7 tokens"  (spec 4 reports 6)

Guard / persistence checks (read-only, run against the installed Control):

from strata_control import tune_knobs as k, presets as P
ctx = k.Context(context_tokens=204800, family="flash-next")
k.check_setting({"--spec": 5}, {}, ctx)   # ['--spec 5: outside the safe values [3, 4]']

Artifacts

Verbatim from the engine log — the --spec 5 start (trimmed) and the cache line, which is identical
for both runs, so the comparison is apples to apples:

[strata] 2026-10-22 01:21:41 engine started: ...\components\engine\0.1.47-windows-x86_64-cuda-cuda13\strata.exe
         --serve --pack ...\pack --native ...\qwen-nvfp4-gptq-dense.gguf ... --spec 5 ...
strata generate: expert cache auto: 8.01 GiB free, 2048 MiB reserved (+281 MiB for the draft head) -> 2226 slots
strata generate: expert cache 2226 slots, 5.73 GiB of VRAM

Activity

  1. github-actions commented on Oct 23, 2026

    @github-actions

    Thanks for the issue. To keep the tracker readable, every item needs an area prefix in the title and the parts of the issue form filled in. This one is missing:

    • Start the title with its area: [Engine], [GUI], [TUI], [Benchmark], [Docs] or [Other], then a short description. Example: "[Engine] decode stops after 2 minutes".

    Edit the issue (title and description) and this note goes away on its own. If it is still missing parts in 5 days it will be closed, which is not a rejection: fix it and comment /reopen.

    Details: CONTRIBUTING.md

  2. yusheng227507 commented on Oct 23, 2026

    @yusheng227507

    Counter-datapoint on Turing (2× RTX 2080 Ti 22 GB, layer split 26/22, IQ3_XXS, MTP drafter rt, engine 0.1.48 self-built sm_75): --spec 5 clearly regresses here.

    Same standard suite on both arms (3 runs each, 33/33 ok), same engine, only --spec differs:

    metric --spec 4 --spec 5 Δ
    short chat decode tok/s 83.2 74.2 -10.8%
    agent session decode tok/s (last-turn) 86.1 73.7 -14.5%
    4K / 16K prompt tok/s 910.0 / 1600.2 909.3 / 1599.8 ~flat
    agent cache hit 69.3% 69.3% ~flat
    draft acceptance (chat+4K) 61.3% 57.0% -7.2 pp
    offered / req (chat median) 98 117 +19.4%

    Mechanism matches your measurement but ends in the opposite place: at spec 5 the drafter offers deeper windows (+19%), acceptance falls harder (-7.2 pp vs your -3.7 pp), and each verification costs more, so the wider window is net negative. That lands inside the guard's own "4% to 16% slower" band for wider windows — on sm_75 the [3, 4] safe range is correct as measured.

    Two things from this box support your framing rather than argue against it:

    1. Greedy text gate is byte-identical 8/8 (--spec 5 vs --spec 4, temperature 0 / seed 1, exact-comparison recipe) — so it is indeed policy, not capability, exactly as you say.
    2. Your +6.1% (sm_120/NVFP4) and this -10.8..-14.5% (sm_75/IQ) together argue that a global widening to [3, 5] fits one architecture and hurts another. Your suggested per-arch / per-VRAM gating (fix 2) looks like the right shape.

    Paste-ready (in the style of the issue body):

    sm_75 (2× RTX 2080 Ti 22 GB, IQ3_XXS, MTP): --spec 5 regresses (chat -10.8%, agent -14.5%, acceptance -7.2 pp at offered +19%) — [3, 4] kept

  3. Niko1221 commented on Oct 27, 2026

    @Niko1221
    Owner

    Thank you for the measurement. Fixed in 0.2.7: A value outside the tuner's safe range, such as --spec 5, can be saved in a preset and used, with a short warning; only values that are never valid (--spec 6, for example) are still refused, and the automatic tuner itself stays inside the safe range. We recommend, we never force.

    Release: https://github.com/Niko1221/Strata/releases/tag/v0.2.7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-formatTitle or template does not follow CONTRIBUTING.md yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions