Skip to content

apr bench (qwen35 --fast) refuses every run with warmup+iterations > 1: one session reuses its own prefix cache #4445

Closed

Description

@noahgift

What happened

On intel (x86_64, wgpu), apr 0.69.5-rc.1 (5660b0877) running
apr bench ~/models/Qwen3.5-4B-Q4_K_M.gguf --fast --warmup 1 --iterations 3 --json exits 5:

error: Validation failed: qwen35 bench: 6 prompt tokens were served from a cached prefix, not prefilled

With --warmup 0 --iterations 1 the same command measures (wgpu, 32 tokens, TTFT 4372 ms).

Why

In crates/apr-cli/src/commands/bench_qwen35.rs@5660b0877, the warmup and every iteration call session_timed_turn(&mut session, prompt_tokens, ...) on one Qwen35Session with the same prompt. From the second turn on, the session serves the prompt from its prefix cache, so turn.reused != 0 and the guard at line ~78 refuses. The guard is correct: a reused prefix would report decode as prefill+decode. But the loop cannot satisfy it. So on the qwen35 path, any bench with warmup+iterations > 1 always refuses, which includes the defaults (warmup 3, iterations 5).

Fix direction

Give each timed turn a fresh session (or reset its cache) so every turn prefills the whole prompt. Add a test that runs warmup ≥ 1 and iterations ≥ 2 and asserts it succeeds and that reused == 0 on every turn. Prove the test goes red on the current loop.

Impact

infra#1057's per-binary perf ledger can't take a multi-sample number for qwen35 from apr bench on any host.

Found by infra-8d while measuring the v0.69.5-rc.1 ledger rows.

Activity

  1. added this to the 0.70.1 milestone on Sep 25, 2026
  2. noahgift commented on Sep 25, 2026

    @noahgift
    MemberAuthor

    Repro on the v0.69.5-rc.1 release assets (aprender-36, 2026-09-25 ~16:50Z): apr bench refuses any qwen35 run with more than one pass

    • Binaries: the sha-verified rc.1 assets, apr 0.69.5-rc.1 (5660b0877).
      • yoga: x86_64-cuda, sha256 d7dc9fcd…, RTX 4060 Laptop.
      • mini: aarch64-darwin-cpu, sha256 3d036e1a….
    • Model: Qwen3.5-4B-Q4_K_M.gguf, sha256 00fe7986ff5f…, the same file on both hosts.
    • Command: apr bench $M --fast --json --offline [args]
    args yoga mini
    (defaults: warmup 3, iterations 5) rc 5 rc 5
    --warmup 0 --iterations 1 rc 0 (10.2 tok/s, TTFT 569 ms) rc 5, but only from the H12 throughput floor (2.5 tok/s on CPU); the JSON is complete
    --warmup 0 --iterations 2 rc 5 –
    --warmup 1 --iterations 1 rc 5 –
    --prompt Explain_the_theory_of_relativity_in_detail rc 5 ("10 prompt tokens…") –

    Error on every refused run: error: Validation failed: qwen35 bench: N prompt tokens were served from a cached prefix, not prefilled. N is the prompt length: 6 for the default prompt, 10 for the one above.

    The failure is deterministic and follows the pass count, not the prompt or the host. On the second pass over the same prompt, the prefix cache serves the prompt, and the bench's own prefill guard rejects it. That makes n>1 qwen35 measurements impossible with apr bench on rc.1. The only way around it was single-pass runs, which is what the infra#1057 ledger rows for yoga and mini are.

    Artifacts are in /mnt/nvme-raid0/tmp/rc1-bench/ on lambda-vector (4b-*-n5-REFUSED.*, 4b-*-single.*, README.md).

  3. noahgift commented on Sep 25, 2026

    @noahgift
    MemberAuthor

    Fix: 65fd97317 on fix/4445-bench-fresh-prefill, based on batch/0.69.4-epic-4249 (#4318). The bug comes from #4214's checkpoint resume (71d3477), which only that branch carries. main, B1r and car/0.70.0 don't reuse a repeated prompt.

    Session::forget_prefix() drops the held positions and the checkpoint. The bench calls it before each warmup and each timed turn, so every turn prefills the whole prompt, and the reused != 0 guard stays unchanged.

    Proof:

    • bench_on_qwen35_times_session_generate (real Qwen3.5-0.8B, warmup 1, iterations 2) fails without the calls, with the error from this issue (16 prompt tokens were served from a cached prefix), and passes with them.
    • New unit test forget_prefix_makes_the_same_prompt_prefill_whole_again fails if forget_prefix keeps the checkpoint.
    • fmt and clippy -D warnings are clean on aprender-serve and apr-cli.

    Routing (the fix goes onto #4318 for rc.2, or waits for the 0.70.1 stage) is the cop's decision. — aprender-ea

  4. added a commit that references this issue on Sep 28, 2026
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions