Skip to content

apr serve on CUDA: batch concurrent Qwen3.5 requests correctly #5312

Open
0 of 2 issues completed
Open

Description

@noahgift

Scope: Concurrent serving on CUDA: a batch scheduler over a BatchEngine trait, a multi-slot greedy step, batched GEMV that is row-equal to the single-row kernels, CUDA-graph decode, and the fix for garbage output when m>1.

Why it matters: apr serve runs Qwen3.5 one request at a time, so the serving-shape (V3) bar cannot pass, and the old batched path produced garbage.

Done when: At c=4/8/16 on Qwen3.5-4B, apr serve forms batches (m_formed>0), every reply equals the single-request reply token for token, and aggregate tok/s rises with c, shown by a perf041 witness on lambda.

Epic: #3994. Folded under triage rule 7b (2026-10-14): each row below was its own issue, closed as not planned and kept here as a sub-ticket. Reopen an issue when its work starts or its PR opens.

Sub-tickets

Activity

  1. added this to the 0.71.0 milestone on Oct 14, 2026
  2. changed the title [-]MASTER E3 0.71: folded sub-tickets (epic #3994)[/-] [+]apr serve on CUDA: batch concurrent Qwen3.5 requests correctly[/+] on Oct 14, 2026
  3. modified the milestones: 0.71.0, 0.72.0 on Oct 14, 2026
  4. noahgift commented on Oct 14, 2026

    @noahgift
    MemberAuthor

    Merged into #5329 (apr serve request path: BPE tokenization, graceful drain, no 500 on oversize prompts): this ticket and its sub-ticket rows are now listed there under "Merged in". Closed as not planned under triage rule 7b; reopen it when its own work starts or a PR opens.

  5. noahgift commented on Oct 14, 2026

    @noahgift
    MemberAuthor

    0.72 floor row, closed in error

  6. noahgift commented on Oct 15, 2026

    @noahgift
    MemberAuthor

    Acceptance check against main @ 8f601ca. #5364 (merge 6f1c82d) is an ancestor of it. This is evidence only; the issue stays open.

    Done when: at c=4/8/16 on Qwen3.5-4B, apr serve forms batches (m_formed>0), every reply equals the single-request reply token for token, and aggregate tok/s rises with c, shown by a perf041 witness on the release host.

    Item On main? Evidence
    Batch scheduler over a BatchEngine trait code: yes api/cuda_batch_scheduler.rs. cuda_batch_scheduler_engine_tests.rs::concurrent_requests_form_one_batch_of_c_over_the_trait runs host-only, in CI
    Multi-slot greedy step, serve wiring (16 slots) code: yes qwen35_batch_engine.rs, forward_qwen35_cuda_batch*.rs, serve/server.rs
    Batched GEMV row-equal to single-row kernels code: yes Q4_K/Q5_K/Q6_K/Q8_0 batched GEMVs and tests_batched_mwv.rs. The PTX of each single-row kernel was compared at base and head (seat-1 review)
    CUDA-graph decode code: yes forward_qwen35_cuda_batch_graph.rs. the_captured_batched_step_is_bitwise_the_eager_one
    m>1 garbage fixed (token-equal to solo) device run: GB10 host only, Qwen3.5-0.8B four_concurrent_requests_batch_and_match_their_solo_turns, batched_greedy_streams_equal_each_slot_decoded_alone and siblings. They need the 0.8B file and a CUDA device and print SKIP otherwise, so CI's lib run exercises none of them. The commit bodies cite GB10 runs; no review re-ran them on a GPU
    m_formed>0 at c=4/8/16 on Qwen3.5-4B NOT MEASURED No Qwen3.5 witness under the release host's evidence/perf041/ directory. The files there (witness.json 2026-10-07, marker.json, witness-v3.0-rule.json) are all qwen2.5-coder-1.5b and predate #5364. The device tests use max_batch: 4 on 0.8B
    Replies token-equal to solo at c=4/8/16 on 4B NOT MEASURED same
    Aggregate tok/s rises with c NOT MEASURED The GB10 ms/step figures in the commit bodies are per-phase, 0.8B, m=4 only

    Missing: one perf041 witness on the release host for Qwen3.5-4B (model sha256 recorded) at c in {1,4,8,16}, from an apr built at a commit that contains 6f1c82d, showing m_formed, per-request token equality against c=1, and aggregate tok/s. The code items are on main; the outcome in the title is not yet measured on the release host.

  7. noahgift commented on Oct 15, 2026

    @noahgift
    MemberAuthor

    Qwen3.5-4B measurement (the remaining acceptance item): measured, ADMISSIBLE ×3.

    Setup: apr serve run Qwen3.5-4B-Q4_K_M.gguf --gpu-layers all on an RTX 4090, CUDA_BATCH_WINDOW_MS=200, then apr test llm parity --ladder 1,4,8,16 and apr test llm shape-check. The binary is apr 0.71.3 (8f601caff2), built with --features cuda, sha256 26073455f173…. The model's sha256 is 00fe7986ff5f….

    The server log shows the CUDA backend for the 4090 (qwen35 hybrid forward), CONTINUOUS BATCHING: max_batch=16, window=200ms, and Batch m=N done lines.

    c m_formed result token-equal to m=1 summed decode tok/s (run 1 / 2 / 3)
    1 1 PASS 128/128 74.9 / 81.3 / 79.7
    4 4 PASS 128/128 171.3 / 168.9 / 169.5
    8 8 PASS 128/128 196.1 / 220.6 / 219.6
    16 16 PASS 128/128 202.6 / 234.3 / 233.1
    • S7 (c=4 per-request decode ≥ 50% of c=1): 57% / 52% / 53%. It passes, but runs 2 and 3 clear the floor by only 2–3 points.
    • Aggregate: about 2.2× at c=4 and about 2.9× at c=16, but it flattens from c=8 to c=16 (+3% to +6%).

    The witnesses are committed on branch PMAT-5312-qwen35-4b-witness (7831932) as witness-qwen35-4b-batched-run{1,2,3}.json beside the existing perf041 4090 witnesses. No PR is open yet. I'm leaving the issue open.

    Disclosure from review: in run 2's c=8 band, two slots (i=6 and i=7) have bit-identical decode_tok_s values (27.574169291500333). The per-slot rate is 127 ÷ (last − first client-side token arrival) at nanosecond resolution, and every slot comes from its own HTTP stream; I found no code path that copies a rate between slots. All 8 slots in that band agree to within 1.8×10⁻⁵, which fits lockstep batched decode, but an exact match is unlikely, and I have no proven cause. It is the only duplicate across the 3 runs × 4 bands. Nothing reported here uses slots 6 and 7 on their own (the band value is the median), and the summed figures above include them as measured.

  8. noahgift commented on Oct 15, 2026

    @noahgift
    MemberAuthor

    Folded into #5329 by the cop batch at 10:30Z (triage cap 100; clerk proposal 1015 row). Tracked as a checklist row there; reopen if it needs its own owner.

  9. noahgift commented on Oct 15, 2026

    @noahgift
    MemberAuthor

    Reopened by the cop: this is a protected 0.72 floor row (handoff/protected-072-floor.txt), so it must not be folded. The 10:30Z fold was the cop's error. Its master link stays as a cross-ref.

  10. noahgift commented on Oct 22, 2026

    @noahgift
    MemberAuthor

    Tracked in #5348 (triage 2026-10-22).

  11. modified the milestones: 0.72.0, 0.73.0 on Oct 22, 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

    P1High priorityenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions