Skip to content

talker: reserve prefill compute buffer at load time (max_prefill_tokens) - #16

Open
DerGary wants to merge 1 commit into
ServeurpersoCom:masterfrom
DerGary:talker-prefill-warmup-reserve
Open

talker: reserve prefill compute buffer at load time (max_prefill_tokens)#16
DerGary wants to merge 1 commit into
ServeurpersoCom:masterfrom
DerGary:talker-prefill-warmup-reserve

Conversation

@DerGary

@DerGary DerGary commented Jul 23, 2026

Copy link
Copy Markdown

Summary

ggml_backend_sched grows its compute/activation buffer to fit the largest graph it has ever built, and never shrinks it back. The talker prefill graph is shaped by the KV-padded context length (GGML_PAD(T_ctx, 256), see talker_forward_core), so the first prefill that lands in a new 256-token bucket ratchets VRAM usage up permanently. In a GPU-memory-constrained deployment (multiple models sharing one card) this makes VRAM usage on tts-server unpredictable: it grows the first time a long-enough prompt arrives on live traffic instead of being fixed at startup.

This mirrors how llama.cpp handles the same problem for --ctx-size: reserve the worst-case compute buffer once, up front, via an explicit reservation pass, instead of letting it grow lazily.

Changes

  • qwen.h: bump QT_ABI_VERSION to 4, add qt_init_params.max_prefill_tokens (0 = disabled, matches previous behavior for zero-initialized older callers).
  • qwen.cpp / pipeline-tts.h / pipeline-tts.cpp: thread the new field through to pipeline_tts_load, which — when max_prefill_tokens > 0 — runs one throwaway talker_forward_prefill call on KV set 0 with a zero-filled embedding of that length (clamped to the KV cache's max_seq_len), discards the result, and resets the KV set. This forces the scheduler to reserve the compute buffer for that prefill bucket before the server starts accepting connections.
  • tools/tts-server.cpp: new --max-prefill-tokens <n> CLI flag wiring into the above.

Failure to reserve (almost always CUDA OOM) is fatal, not a logged warning: this is a capacity check, not a performance warmup. A deployment that cannot afford its own configured worst case should refuse to start loudly at boot, instead of succeeding here and then failing unpredictably mid conversation once a live request finally needs that memory.

Testing

Deployed on a memory-constrained Pascal (sm_61, 8GB) box sharing the GPU with other models. Confirmed via nvidia-smi:

  • Without a reservation: VRAM ratcheted up ~10MB the first time a request crossed into a new 256-token decode/prefill bucket, and never came back down.
  • With --max-prefill-tokens 512: the log line [Pipeline] Reserving talker prefill compute buffer for T=512... appears before [Server] listening on ..., and VRAM is flat across repeated requests within that bucket.
  • Server still starts and serves correctly with --max-prefill-tokens omitted (default 0, no behavior change).
  • Confirmed the fatal path is reachable: a deployment cannot silently continue past a failed reservation.

Note: this only reserves the prefill bucket (scales with input text length). Decode/codec buffers (scale with output audio length) still grow lazily the first time a longer generation is requested — out of scope for this PR, handled operationally on our end with an additional end-to-end warmup synthesis at the application layer.

@DerGary
DerGary force-pushed the talker-prefill-warmup-reserve branch from 6cf5054 to 8edef25 Compare July 23, 2026 12:15
@DerGary DerGary changed the title talker: reserve prefill compute buffer at load time (warmup) talker: reserve prefill compute buffer at load time (max_prefill_tokens) Jul 23, 2026
@DerGary

DerGary commented Jul 25, 2026

Copy link
Copy Markdown
Author

@ServeurpersoCom friendly ping — would appreciate a look when you have a moment.

ggml_backend_sched grows its compute/activation buffer to fit the
largest graph it has ever built, and never shrinks it back. The talker
prefill graph is shaped by the KV-padded context length
(GGML_PAD(T_ctx, 256), see talker_forward_core), so the first prefill
that lands in a new 256-token bucket ratchets VRAM usage up
permanently. In a GPU-memory-constrained deployment (multiple models
sharing one card) this makes VRAM usage on tts-server unpredictable:
it grows the first time a long-enough prompt arrives on live traffic
instead of being fixed at startup.

This mirrors how llama.cpp handles the same problem for --ctx-size:
reserve the worst-case compute buffer once, up front, via an explicit
reservation pass, instead of letting it grow lazily.

- qwen.h: bump QT_ABI_VERSION to 4, add
  qt_init_params.max_prefill_tokens (0 = disabled, matches previous
  behavior for zero-initialized older callers).
- qwen.cpp / pipeline-tts.h / pipeline-tts.cpp: thread the new field
  through to pipeline_tts_load, which -- when max_prefill_tokens > 0 --
  runs one throwaway talker_forward_prefill call on KV set 0 with a
  zero-filled embedding of that length (clamped to the KV cache's
  max_seq_len), discards the result, and resets the KV set. This
  forces the scheduler to reserve the compute buffer for that prefill
  bucket before the server starts accepting connections.
- tools/tts-server.cpp: new --max-prefill-tokens <n> CLI flag wiring
  into the above.

Failure to reserve (almost always CUDA OOM) is fatal, not a logged
warning: this is a capacity check, not a performance warmup. A
deployment that cannot afford its own configured worst case should
refuse to start loudly at boot, instead of succeeding here and then
failing unpredictably mid conversation once a live request finally
needs that memory.

Tested on a memory-constrained Pascal (sm_61, 8GB) box sharing the GPU
with other models. Confirmed via nvidia-smi:
- Without a reservation: VRAM ratcheted up ~10MB the first time a
  request crossed into a new 256-token decode/prefill bucket, and
  never came back down.
- With --max-prefill-tokens 512: the log line "[Pipeline] Reserving
  talker prefill compute buffer for T=512..." appears before
  "[Server] listening on ...", and VRAM is flat across repeated
  requests within that bucket.
- Server still starts and serves correctly with --max-prefill-tokens
  omitted (default 0, no behavior change).

Note: this only reserves the prefill bucket (scales with input text
length). Decode/codec buffers (scale with output audio length) still
grow lazily the first time a longer generation is requested -- out of
scope for this change.
@DerGary
DerGary force-pushed the talker-prefill-warmup-reserve branch from 8edef25 to ae0362a Compare July 25, 2026 09:32
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