Skip to content

serve: up to N concurrent replies on one GPU - #7

Closed
x-Pepsy wants to merge 1 commit into
RustingStudio:mainfrom
x-Pepsy:parallel-serve
Closed

x-Pepsy wants to merge 1 commit into
RustingStudio:mainfrom
x-Pepsy:parallel-serve

Conversation

@x-Pepsy

@x-Pepsy x-Pepsy commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

The server runs one request at a time (handle decodes until the reply is done), so four clients means four people waiting for their turn. This lets up to --parallel N replies make progress at once on one GPU.

The model already parked a conversation's state in host memory when another history replaced it, so I reused that: a reply prefills and parks its state when it arrives, then each turn of the scheduler adopts the state, generates PAR_QUANTUM tokens and parks it again. The tokens are unchanged; the only thing that moves is the state.

I picked the quantum by measuring. With four sequences, one token a turn loses about half the aggregate throughput to the state copies (23 vs 39 tok/s), four tokens gets most of it back (35), and eight is within noise of one at a time (39). So the default is 8.

Numbers on a 2060 6 GB:

  • four streaming requests, 48 tokens: 4.7-7.3 s each, 1-3.8 s to the first byte, all done.
  • four 64-token replies one at a time: 9.2 s total, the last client waits 8.7 s.
  • the same four with --parallel 4: 9.8 s total, all four finish around 8-9 s, so every stream moves instead of queueing.

Limitations, all deliberate for a first cut:

  • MTP is off in this mode. A parked state has the KV and the recurrent state but not the draft head's pending drafts.
  • Prefill still runs when a request arrives, so the fourth client's first byte waits for the three prefills ahead of it.
  • A split model falls back to serial: parking is per model and each part would need its own copy.
  • --parallel defaults to 1, so nothing changes unless asked.

tests/concurrency.rs runs four sequences interleaved at q = 1, 4 and 8 and checks the tokens match generating them one at a time, so the state round-trip is covered.

The new --parallel N keeps each reply KV and recurrent state parked in host memory and lets the sequences take turns, PAR_QUANTUM tokens each. On a 2060 with four sequences, quantum 1 loses half the aggregate throughput to the state copies and quantum 8 stays within a few percent of generating one reply at a time, so each stream runs at about 1/4 speed instead of 1/8.

Prefill happens when the request arrives, before the reply is parked. The system-prompt stash is dropped in this mode (its KV rows belong to whichever sequence ran last). MTP is off, because a parked state does not carry the draft head, and a split model falls back to one reply at a time. The default stays serial.

Four streaming requests of 48 tokens took 4.7-7.3 s each with 1-3.8 s to the first byte. Four 64-token replies one at a time take 9.2 s for the same work, with the last client waiting 8.7 s; with four in parallel they finish together in about 9.8 s.

tests/concurrency.rs interleaves four sequences and checks their tokens against generating them one at a time (q = 1, 4 and 8).
@x-Pepsy x-Pepsy closed this Nov 6, 2026
Vasyl-Trefilov pushed a commit that referenced this pull request Nov 8, 2026
* docs(parity): record V100 Vulkan parity baseline (M0-T7)

20 prompts x 32 greedy tokens against llama-server 0.9.0-dev build
12110 (cd959bae9) on the V100, parity mode with --fixed-experts.

strict 625/640 = 97.66%, either llama.cpp mode 629/640 = 98.28%,
llama.cpp self-agreement 630/640 = 98.44%: below the 634/640
either-mode bar, recorded as a documented deviation with a second
parity-mode repeat (identical) and the default path (626/630).
No new Xid. Artifact in docs/parity/vk-sm70.json.

* docs: fix the 3060 comparison in the M0-T7 deviation note
Vasyl-Trefilov pushed a commit that referenced this pull request Nov 8, 2026
M0-T7 merged as 7627641 (issue #6, PR #7): the V100 Vulkan baseline is
629/640 either-mode (98.28%), below the 99% bar; the deviation is accepted
and recorded. Add M3-T5 to diagnose the gap (prompts 10/16/17/19) and update
the M3 objective.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant