Skip to content

perf(server): serving-throughput defaults: parallel decode, batched prefill, prompt cache#714

Merged
inureyes merged 3 commits into
mainfrom
perf/628-serving-throughput-defaults
Jul 9, 2026
Merged

perf(server): serving-throughput defaults: parallel decode, batched prefill, prompt cache#714
inureyes merged 3 commits into
mainfrom
perf/628-serving-throughput-defaults

Conversation

@inureyes

@inureyes inureyes commented Jul 9, 2026

Copy link
Copy Markdown
Member

Summary

Ship serving-throughput defaults for multi-client serving: --parallel 4 (batched decode) and --max-batch-prefill 4 (batched prefill), clamped to single-slot for families that cannot batch, plus a --no-prompt-cache opt-out. The batched-decode default is paired with a default --kv-cache-budget auto memory guard. Defaults chosen from measurements on Apple M1 Ultra. This changes defaults and documentation, not machinery.

Audit first (issue premises vs reality on main)

Per the audit-first mandate (we were burned by a stale premise on #330), each premise was checked against 9808b675 before implementing:

Premise Issue claim Reality on main Action
--parallel / max_batch_size defaults to 1 confirmed (default_value_t = 1, unwrap_or(n_parallel) = 1) changed to 4
--max-batch-prefill defaults to 1 confirmed (default_value_t = 1) changed to 4
prompt cache off unless configured stale: already default-on (--prompt-cache-enabled default true, PromptCacheConfig::default().enabled = true, 2 GiB budget, APC on; satisfied by #228) kept on; added --no-prompt-cache opt-out

The prompt cache was already default-on. Only the two batching defaults were genuinely off, so only those changed.

What changed

  • --parallel default 1 -> 4 and --max-batch-prefill default 1 -> 4 on both mlxcel-server (src/bin/mlx_server.rs) and mlxcel serve (src/main.rs), plus the ServerConfig::default() / ServerStartupConfig::default() struct defaults.
  • New --no-prompt-cache flag (both binaries) as the highest-precedence opt-out for the default-on prompt cache.
  • supports_batching() gate: the worker (src/server/model_worker.rs) clamps the effective decode batch to 1 for SSM / hybrid / mixed-cache families, so the default is safe for every architecture and the scheduler log reports the effective value.
  • Default memory guard (security): --kv-cache-budget now defaults to auto, so the Phase 5: Block-budget admission, eviction, and preemption #122 paged block-budget admission bounds KV for the concurrent batch and returns clean backpressure instead of an OOM abort. A new PagedBudgetDirective::Disabled variant plus --kv-cache-budget none / 0 is the escape hatch that leaves the pool unbounded; the guard is inert on the dense decode backend (pre-existing behavior).
  • Docs: docs/CONTINUOUS_BATCHING.md (defaults + measured tuning table + budget guard), docs/environment-variables.md (context-sizing note, MLXCEL_KV_CACHE_BUDGET default), CHANGELOG.md (behavior change + --ctx-size migration note), and a dated docs/benchmark_results/serving-throughput-defaults-m1u-2026-07-09.md.
  • Tests: config-default assertions updated (max_batch_prefill == 4, kv_cache_budget == Some(Auto)); default-parse, --no-prompt-cache, --kv-cache-budget auto/none/0/bytes, and PagedBudgetDirective::Disabled tests added; ServeArgs fixture updated.

Measured (Apple M1 Ultra, Metal; meta-llama-3.1-8b-instruct-4bit, 512-token prompt, 128 tokens)

clients aggregate tok/s (-p1) aggregate tok/s (batched) TTFT mean ms (-p1) TTFT mean ms (batched)
1 56.6 56.8 889 783
2 71.5 99.9 1150 114
4 65.6 107.9 3257 189
8 62.8 105.1 (-p8) 7514 348 (-p8)

At 4 clients: 1.90x aggregate throughput and ~17x lower mean TTFT under load, single-client unchanged. Batched prefill halves cold-prefill TTFT (4215 -> 2142 ms). B=8 on the 8B model: 0 failures, peak RSS 4.4 GB (no OOM on 128 GB). The 8-client batched row was measured at --parallel 8; the shipped default is --parallel 4 (batch caps at 4).

Test plan

Measured / verified here (Apple M1 Ultra, Metal):

  • cargo fmt --all -- --check clean; cargo clippy --lib --bins --tests -- -D warnings clean.
  • Lib tests server::config::tests + server::startup::tests + execution::memory_estimate::tests (85 passed).
  • mlxcel-server tests: default-parse, --no-prompt-cache, --kv-cache-budget auto/none/0/bytes (9 passed); mlxcel commands::serve::tests (11 passed).
  • Real-model: fresh server logs max_batch_size=4, max_batch_prefill=4 and Paged KV block budget: ... blocks (auto default); --kv-cache-budget none logs the clean disable; mamba2 (non-batching) clamps 4 -> 1; --no-prompt-cache disables the store; /v1/cache/stats responds; concurrent requests served with 0 failures.
  • Prompt-cache hit path: mlxcel_prompt_cache_hits_total increments (28 in the scaling run).

Pending a CUDA session (GB10 not available this session):

  • Aggregate decode tok/s at 4 clients >= 2.5x the B=1 config on GB10 (measured 1.90x on M1 Ultra; GB10's higher bandwidth should exceed 2.5x).
  • cargo test --features cuda (config default tests).

Closes #628

The MLX serving path implements continuous batching, batched prefill, and a prompt-prefix KV cache, but the shipped defaults disabled the first two: `--parallel` (and thus `max_batch_size`) defaulted to 1 and `--max-batch-prefill` to 1, so out of the box the server decoded and prefilled one sequence at a time. The prompt cache was audited and found already default-on (2 GiB budget, APC on), so only the two batching defaults were genuinely off; the third premise was stale.

Change the defaults for `mlxcel-server` and `mlxcel serve` to `--parallel 4` and `--max-batch-prefill 4`, add `--no-prompt-cache` as a clean opt-out for the default-on prompt cache, and clamp the effective decode batch to 1 for families that cannot batch (SSM / hybrid / mixed-cache, `supports_batching() == false`) so the default is safe for every architecture. `--parallel 1`, `--no-batch`, `--max-batch-prefill 1`, and `--no-prompt-cache` restore the previous single-client behavior. Batched prefill only engages for families that opt into `supports_batched_prefill()`; others fall back to sequential prefill.

Measured on Apple M1 Ultra (Metal, `meta-llama-3.1-8b-instruct-4bit`, `scripts/bench_serving_concurrency.py`): at 4 concurrent clients the new default delivers 1.90x the single-client aggregate throughput (107.9 vs 56.8 tok/s) and cuts mean time-to-first-token under load ~17x (3257 ms to 189 ms), with single-client throughput unchanged and no OOM at B=8. Batched prefill halves cold-prefill TTFT (4215 to 2142 ms). The GB10 `>= 2.5x`-at-4-clients acceptance target and `cargo test --features cuda` are backend-specific and remain pending a CUDA session; full data and the audit are in docs/benchmark_results/serving-throughput-defaults-m1u-2026-07-09.md.

Docs (docs/CONTINUOUS_BATCHING.md tuning table, docs/environment-variables.md context-sizing note) and CHANGELOG updated; config-default unit tests adjusted and a default-parse plus no-prompt-cache test added.

Closes #628
@inureyes inureyes added type:performance Performance improvements priority:medium Medium priority area:inference Generation, sampling, decoding (incl. speculative, DRY) status:review Under review labels Jul 9, 2026
inureyes added 2 commits July 9, 2026 21:53
…mory guard

Pairs the #628 batched-decode default (`--parallel 4`) with a default `--kv-cache-budget auto` so the concurrent batch cannot run four full-context sequences into an uncatchable OOM abort. The #122 paged block-budget admission now bounds KV and returns clean backpressure by default; on the dense decode backend the budget is inert, preserving pre-existing behaviour. The default is set both in the CLI arg (`default_value = "auto"` on `mlxcel-server` and `mlxcel serve`) and in `ServerConfig::default()` / `ServerStartupConfig::default()`.

Adds an explicit escape hatch: `--kv-cache-budget none` (also `off` / `disabled` / `unbounded` / `0`) leaves the pool unbounded via a new `PagedBudgetDirective::Disabled` variant, resolved before the block-count path so it skips the "geometry unavailable" warning. `auto` and a raw byte count keep their meaning.

Also folds in the review-batch fixes: refresh the stale `max_batch_size` doc comment (was "typically 1", now 4 with the non-batching clamp noted); relabel the docs/CONTINUOUS_BATCHING.md scaling table so the 8-client row is attributed to its `--parallel 8` measurement rather than the `--parallel 4` default; add the `--ctx-size` migration note to CHANGELOG (an explicit small ctx-size is now divided across 4 slots and can trip the 512-token/slot floor); and pin the new defaults with tests: `max_batch_prefill == 4` and `kv_cache_budget == Some(Auto)` in `ServerConfig::default()`, the `auto` CLI default plus the `none` / `0` / bytes parse paths, the `Disabled` FromStr keywords, and `resolve_paged_block_budget(Disabled) == None`.

Verified on Apple M1 Ultra: lib config/startup/memory_estimate tests (85 passed) and `mlxcel-server` tests (9 passed) green; a live `qwen2.5-0.5b-bf16` run logs `Paged KV block budget: ... blocks` by default and `--kv-cache-budget disabled` with `none`, and `/v1/cache/stats` responds.

Refs #628
The paged block-pool budget section still said the default was unbounded, which went stale when cb3ec59 changed the shipped default to `auto` so the batched-decode default cannot run concurrent full-context sequences into an OOM abort. Update the flag/env description to match config.rs and the already-updated CONTINUOUS_BATCHING.md / environment-variables.md, and note `none`/`0` as the escape hatch back to an unbounded pool.

Validation:
- cargo fmt --all -- --check
- cargo clippy --lib --bins --tests --features metal,accelerate -- -D warnings

Refs #628
@inureyes inureyes added status:done Completed and removed status:review Under review labels Jul 9, 2026
@inureyes inureyes merged commit 7122909 into main Jul 9, 2026
5 checks passed
@inureyes inureyes deleted the perf/628-serving-throughput-defaults branch July 9, 2026 13:10
inureyes added a commit that referenced this pull request Jul 9, 2026
…720)

## Summary

Resolves the PR #709 review finding that `paged_decode_attention_pooled` has no `mlxcel-server` caller. The decision recorded here is to retire the pooled decode entry point (and its `select_pooled_paged_dispatch` selector) to a library-only API rather than wire it into the scheduler decode.

## Why retire (occupancy evidence, Apple M1 Ultra)

The fused kernel's winning island is single-slab only. A slab is `POOL_SLAB_BLOCKS = 32` block rows (`src/lib/mlxcel-core/src/cache/paged.rs`) and a block holds `DEFAULT_PAGED_BLOCK_SIZE = 32` tokens (`src/server/batch/scheduler.rs`), so one slab is 32 x 32 = 1024 token rows per layer across every sequence resident in the shared pool (`PagedBlockPool::slab_count` returns the layer's whole slab-list length). The selector requires `slab_count <= 1` and `batch_size >= 4`, so the pool stays native only while `B * ceil(len / 32) <= 32`. At the shipped `--parallel 4` default (#714, `n_parallel = 4`) that is `len <= 256` total tokens per sequence, counting prompt, chat template, and generated tokens.

That island is negligible in production. The #714 serving-throughput bench drives 512-token prompts, which at `B = 4` need `4 x 16 = 64` block rows = 2 slabs, so every layer is multi-slab from the first decode step (the #331 bench table in ADR 0001 shows every `B >= 4` row at `ctx >= 512` reading `declined`). The pool only appends slabs (#235: existing slabs are never freed), so a request that starts inside the island leaves it permanently within its first few dozen generated tokens. Wiring the pooled path in would thread a new batched-decode arm through every transformer family (high blast radius, jitter-class parity risk) for that transient sliver. Since the pooled path has no server caller, its measured production occupancy is definitionally zero today, so the closed-form geometry is stronger evidence than a thermally-noisy instrumented run.

## What changed

- `src/lib/mlxcel-core/src/layers.rs`: library-only doc comments on `paged_decode_attention_pooled`, `select_pooled_paged_dispatch`, the `MLXCEL_PAGED_ATTENTION_NATIVE` override (`NativePagedOverride`), and the per-shape dispatch memo (`PagedDispatchCache`), stating they are not on the `mlxcel-server` decode path and are kept for external mlxcel-core consumers and `examples/paged_attention_kernel_bench.rs`.
- `docs/adr/0001-paged-attention-gather-vs-fused-kernel.md`: new decision record with the occupancy derivation, the geometry math, the chosen exit, and what would reopen the question.
- `docs/environment-variables.md`, `docs/turbo-kv-cache.md`: `MLXCEL_PAGED_ATTENTION_NATIVE` is now described as a library-consumer control and A/B pin for the bench, not a server knob.

## What was kept vs removed

- Kept: the fused kernel (`PagedBlockPool::paged_decode_fused`), the selector, the memo, the env override, and `examples/paged_attention_kernel_bench.rs` (tested, benchmarked library surface).
- Kept: `use_native_paged_kernel` scheduler request. It also gates the live `paged_decode_attention_dense_compat` block-table decode path (`src/models/llama3.rs` and the other families), so it is shared plumbing, not dead code. No plumbing was removed.
- Removed: nothing. The pooled function, selector, kernel, and bench all remain.

## Test plan

- [x] `cargo fmt --all -- --check`
- [x] `cargo check --lib --tests --features metal,accelerate`
- [x] `cargo clippy --lib --tests --features metal,accelerate -- -D warnings`
- [x] `cargo test --release --features metal,accelerate -p mlxcel-core layers::tests::` (53 passed: selector island, boundaries, override pinning)
- [x] `cargo test --release --features metal,accelerate -p mlxcel-core ffi_tests::test_fused_paged_decode` (2 passed: gather parity over 200 steps, GQA + batched)

Hardware: Apple M1 Ultra.

Closes #710
inureyes added a commit that referenced this pull request Jul 10, 2026
…ll (#723)

## Summary
Raises the CUDA quantized-matmul (`qmm_sm80`) CTA tile M cap from 64 to 128 on Blackwell consumer GPUs (sm_120/121, `cc_major >= 12`), recovering +31-38% prefill throughput on GB10. Part of epic #623.

Closes #637

## Root cause
`qmm_sm80`'s `make_cta_tiler` caps `tile_m` at 64 (Ampere-tuned). On GB10 (sm_121) the Ampere tile leaves the SMs idle: ncu shows **35-47% SM / 52-68% memory throughput**, and 4bit prefill runs at **0.55x** the bf16 ceiling. (The sm_90 Hopper kernel isn't compiled for arch `121`, so Blackwell falls to the Ampere `qmm_sm80`.)

## Fix
Arch-gated `make_cta_tiler` (overlay `qmm_sm80.cu`): `tile_m` cap = 128 when `device.compute_capability_major() >= 12`, else 64 (stock). A tile sweep confirmed `tile_m` is the **only** safely-tunable axis (wider `tile_n` / deeper `tile_k` break the fixed-MMA shared-memory layout -> JIT failure). Adds `MLXCEL_QMM_TILE_M/N/K` env overrides as a tuning hatch.

## Benchmark (GB10, full detail committed under `docs/benchmark_results/`)
| model | forced tile_m=64 | arch-gated default (128) | delta |
|---|---|---|---|
| llama-3.1-8b-4bit prefill @8192 | 2213 | 3054 | **+38%** |
| qwen2.5-7b-4bit prefill @8192 | 2410 | 3168 | **+31%** |

- Greedy parity: default(128) vs forced(64) generated tokens **byte-identical**.
- Decode (m=1): **unaffected** (single-seq decode takes the qmv path, never reaches `qmm_sm80`).
- 0.55x -> 0.72x of the bf16 ceiling.

## Scope / not addressed
- **Addressed**: large-M prefill + batched prefill on sm_120/121 (+31-38%).
- **NOT addressed**: small-M batched decode (`M*B` in [2,8)) still uses per-row qmv (no weight amortization); the `tile_m` cap doesn't raise small-M tiles. This is the gap behind #714's `--parallel 4` CUDA regression and needs a dedicated small-M kernel (upstream MLX). #714's `--parallel` default should be made backend-aware until then.

## Validation
- C++ overlay change only (no Rust touched). Validated by greedy parity + prefill benchmark (the right gates for a GEMM tile change).
- `make release-cuda` builds clean. The arch gate uses runtime `compute_capability_major()`, so it works for both `121` and `121a` builds (both cc 12.1 at runtime).

## Acceptance criteria (#637)
- [x] Findings doc in `docs/benchmark_results/` (achieved-vs-ceiling, sm_121 dispatch map, ncu roofline, go/no-go).
- [x] >= 25% prefill @8192 on llama-3.1-8b-4bit on GB10 (measured **+38%**), no decode regression, parity green.
- [x] Both `121`/`121a` build (arch-gate is runtime cc, not compile-time arch string).
inureyes added a commit that referenced this pull request Jul 10, 2026
With --max-batch-prefill 4 now the default (#628), the server's padded batched-prefill path (run_padded_batched_prefill) engages out of the box for supports_batched_prefill() families and, for mixed-length prompts, runs a single unchunked [B, padded_len] forward that materializes a stacked [B, L, L] FP32 attention mask, an O(B*L^2) transient that ignores prefill_chunk_size. Four concurrent 8k prompts build a [4, 8192, 8192] FP32 mask (~1 GiB) far above the sequential chunked prefill working set, and on the fail-fast serving path an allocation failure is an uncatchable MLX C++ throw that aborts the whole server. This is an availability edge the --kv-cache-budget guard does not model: that budget bounds steady-state KV, not this prefill transient.

Bound the drained batched-prefill window by total padded tokens (rows * max_len), not just row count. A cohort of B >= 2 rows padded to L keeps B*L within max_batch_prefill_tokens, so the [B, L, L] mask stays within budget^2 / 2 elements (~2*budget^2 bytes at FP32). Rows past the budget stay queued and prefill on a later tick: short ones re-batch, long ones take the chunked single-sequence path. A head prompt too long to join a two-row batch (2*head_len > budget) skips the batched path entirely via a dispatch-time guard, keeping the attention mask chunked to [chunk, L] instead of an unchunked [L, L]. The drain always takes the head so it makes forward progress (no livelock).

The budget is configurable via --max-batch-prefill-tokens (CLI) and MLXCEL_MAX_BATCH_PREFILL_TOKENS (env), with the flag taking precedence, then the env, then the derived default. The default is max_batch_prefill * prefill_chunk_size (the shipped 4 * 512 = 2048), which bounds the FP32 mask to about 8 MiB while keeping a full batch of chunk-sized prompts eligible, so the #714 short-prompt concurrency case (4 x 512-token = 2048 tokens) still batches unchanged. 0 disables the cap for the pre-#715 unbounded behavior.

The cap logic lives in pure, unit-tested functions in prefill_cohort.rs (batched_window_admits / batched_prefill_window_len / default_batched_prefill_token_budget) covering fits/spills/boundary/uncapped/long-head/default-budget. The scheduler drains via peek_prompt_len + batched_window_admits and gates dispatch with batched_prefill_admits_head; the value threads from ServerConfig through WorkerSchedulerConfig to a with_max_batch_prefill_tokens builder.

Validated: cargo check --lib --tests, cargo test --lib server::batch::prefill_cohort/queue (42 passed, incl. 8 new cap tests), cargo clippy --lib --tests -D warnings, cargo fmt --all --check all clean. The bound is analytic and documented (code comment + docs/CONTINUOUS_BATCHING.md formula); the empirical M1 Ultra 8k-prompt peak-memory and short-prompt TTFT A/B are pending a measurement session (repro commands recorded in the docs).

Closes #715
inureyes added a commit that referenced this pull request Jul 10, 2026
#722)

## Summary

`--max-batch-prefill 4` (the #628 default) engages the server's padded batched-prefill path out of the box, and for mixed-length prompts it runs one unchunked `[B, padded_len]` forward that materializes a stacked `[B, L, L]` FP32 attention mask, an `O(B*L^2)` transient that ignores `prefill_chunk_size`. Four concurrent 8k prompts build a `[4, 8192, 8192]` FP32 mask (~1 GiB) far above the sequential chunked prefill working set, and on the fail-fast serving path an allocation failure is an uncatchable MLX C++ throw that aborts the whole server. This bounds that transient by a padded-token budget while keeping the #714 short-prompt concurrency case unchanged.

## Design and bound

Cap the drained batched-prefill window by total padded tokens (`rows * max_len`), not just row count. A cohort of `B >= 2` rows padded to `L` keeps `B*L` within `max_batch_prefill_tokens`, and since `L <= (B*L)/2` the `[B, L, L]` mask stays within `budget^2 / 2` elements, i.e. `~2*budget^2` bytes at FP32.

- Rows past the budget stay queued and prefill on a later tick: short ones re-batch, long ones take the chunked single-sequence path.
- A head prompt too long to join a two-row batch (`2*head_len > budget`) skips the batched path entirely via a dispatch-time guard, keeping the mask chunked to `[chunk, L]` instead of an unchunked `[L, L]`. The drain always takes the head, so it makes forward progress (no livelock).
- Default budget = `2 * max_batch_prefill * prefill_chunk_size` (the shipped `2 * 4 * 512 = 4096`), bounding the FP32 mask to ~34 MiB. The #714 case (4 x 512-token = 2048) still batches, with 2x headroom to spare for prompts that tokenize slightly over `prefill_chunk_size`. `0` disables the cap (pre-#715 unbounded behavior).
- Precedence: `--max-batch-prefill-tokens` flag > `MLXCEL_MAX_BATCH_PREFILL_TOKENS` env > derived default.

## What changed

- `src/server/batch/prefill_cohort.rs`: pure cap logic `batched_window_admits` / `batched_prefill_window_len` (test-only) / `default_batched_prefill_token_budget`, plus unit tests (fits/spills/boundary/uncapped/long-head/default-budget).
- `src/server/batch/scheduler.rs`: token-bounded drain in `execute_batched_prefill` (peek + `batched_window_admits`), the `batched_prefill_admits_head` dispatch guard, `resolve_max_batch_prefill_tokens` (env/default), and the `with_max_batch_prefill_tokens` builder. Direct unit tests cover the `configured = Some(_)` precedence branch of `resolve_max_batch_prefill_tokens` and the `batched_prefill_admits_head` boundary (`2 * head_len == budget` admits, `+1` rejects).
- `src/server/batch/queue.rs`: `peek_prompt_len` for the drain/guard.
- Config plumbing: `--max-batch-prefill-tokens` on both binaries (`bin/mlx_server.rs`, `main.rs`, `commands/serve.rs`) threaded `ServerConfig` -> `WorkerSchedulerConfig` (`cli_input.rs`, `startup.rs`, `config.rs`, `model_provider.rs`, `model_worker.rs`) plus the two touched test literals.
- 959bfe1: the empirical short-prompt A/B (see Test plan) caught a boundary regression at the exactly-tight `max_batch_prefill * prefill_chunk_size` default: real ~512-token prompts tokenize slightly over `prefill_chunk_size`, spilling the last row of a 4-client batch and doubling p95 TTFT. The derived default now carries 2x padding headroom (`2 * max_batch_prefill * prefill_chunk_size`, `4096` shipped); the FP32 mask bound stays negligible (~34 MiB) and the 8k-prompt spill path is unaffected.
- Docs: `docs/CONTINUOUS_BATCHING.md` (bound formula + flag), `docs/environment-variables.md` (env var), `CHANGELOG.md`.

## Test plan

Measured here (Apple M1 Ultra, macOS):

- [x] `cargo test --lib -- server::batch::prefill_cohort server::batch::queue`: passing (incl. cap tests, the `resolve_max_batch_prefill_tokens` precedence test, and the `batched_prefill_admits_head` boundary tests).
- [x] `cargo check --lib --tests --features metal,accelerate`: clean.
- [x] `cargo clippy --lib --tests --features metal,accelerate -- -D warnings`: exit 0.
- [x] `cargo fmt --all -- --check`: exit 0.
- [x] Empirical peak-memory A/B: four concurrent ~9.8k-token prompts (`meta-llama-3.1-8b-instruct-4bit`), capped default vs `--max-batch-prefill-tokens 0`. Capped peak RSS 4115 MiB vs uncapped 4375 MiB, first response 3.5x earlier (17.6s vs 62s). Full numbers in the [measurement comment](#722 (comment)).
- [x] Short-prompt TTFT A/B: `scripts/bench_serving_concurrency.py --concurrency 4 --prompt-tokens 512 --max-tokens 128` against main, the pre-headroom-fix budget, and 959bfe1. The pre-fix budget (`2048`) is the boundary regression this A/B caught (p95 TTFT 2.2x); 959bfe1 (`4096`) restores main parity (mean +0.5%, p95 uniform, aggregate -0.7%). Full numbers in the [measurement comment](#722 (comment)).

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

Labels

area:inference Generation, sampling, decoding (incl. speculative, DRY) priority:medium Medium priority status:done Completed type:performance Performance improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(server): serving-throughput defaults: parallel decode, batched prefill, prompt cache

1 participant