Skip to content

[None][perf] disagg: keep GEN prompt_token_ids off the event loop (metadata-only + int32 lazy) - #16475

Closed
hyukn wants to merge 1 commit into
NVIDIA:mainfrom
hyukn:yukunh/disagg-gen-tokids-offloop
Closed

[None][perf] disagg: keep GEN prompt_token_ids off the event loop (metadata-only + int32 lazy)#16475
hyukn wants to merge 1 commit into
NVIDIA:mainfrom
hyukn:yukunh/disagg-gen-tokids-offloop

Conversation

@hyukn

@hyukn hyukn commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

The GEN OpenAIServer front-end is a single asyncio event loop; at high concurrency the ∝ISL prompt-token array per request dominates gen_preprocessing (the queuing "wait" before the engine admits a request). This PR cuts that via two opt-in, correctness-guarded paths (both default off, so no behavior change unless enabled):

  1. gen_tokids_metadata_only — the orchestrator sends the GEN worker only prompt_len; GEN builds a length-only placeholder and generation uses the KV transferred from the context worker. The forward path never dereferences prompt token VALUES (seq_len/position come from prompt_len; the decode boundary token arrives separately via disaggregated_params.first_gen_tokens). Guardrail: taken only when CTX reported a prompt_len AND the request does not read prompt token values (frequency/presence/repetition penalty, echo) — otherwise it falls back to relaying real tokens. Deployment constraints: text-only, non-harmony, GEN KV block reuse OFF.
  2. int32-ndarray lazy submit (correct for ALL requests) — keeps the base64-decoded prompt_token_ids as an int32 ndarray (drops the O(ISL) .tolist() on the GIL-held submit path); the Python list is materialized lazily only if a consumer reads the values. Composes with the base64 relay ([None][perf] disagg: opt-in ctx-side base64 int32 transport for prompt_token_ids #15698) and the ctor memcpy ([None][perf] executor: memcpy int32 token buffer into tle::Request ctor #15211).

Perf evidence (DSv4-Pro disagg, c3120, 60 GPU, GB300; isolated metadata_only ON vs OFF, same build, N=1)

metric effect of metadata_only
gen_preprocessing (GEN server_arrival→engine-arrival) ~3.0s → 211ms (~15×)
throughput +15% req/s
TTFT p50 −34%
SSE p25 unchanged (that gain is the int32-lazy/transport side)
success / cache-hit 99.9% / 0.940 == baseline (conversation affinity intact)

Correctness

  • GSM8K temp=0, N=200, metadata_only ON vs OFF: identical accuracy 0.975 (195/200), 0 errors. Per-question: 198/200 identical; the 2 differences are symmetric (each config wins one) with coherent reasoning — i.e. greedy non-determinism (FP/MTP/batching), not corruption.
  • Mechanism: decode never embeds a placeholder — CTX produces the first token (transferred via first_gen_tokens) and GEN continues from that real token using the transferred KV.

Tests

  • CPU unit tests: int32 laziness + real-value read + pickle round-trip; prompt_inputs ndarray preservation; the metadata-only guardrail predicate. (14 passed)

Follow-ups before ready-for-review

  • Guardrail completeness: confirm no_repeat_ngram_size / prompt_logprobs / the /v1/completions path are covered or N/A.
  • ON-vs-ON control to pin the temp=0 non-determinism floor (optional).

🤖 Generated with Claude Code

…tadata-only + int32 lazy)

Reduces the GEN OpenAIServer's per-request front-end cost -- the ISL-sized
prompt token array on the single asyncio event loop that dominates
gen_preprocessing at high concurrency -- via two opt-in, correctness-guarded
paths.

1. gen_tokids_metadata_only: the orchestrator sends the GEN worker only
   prompt_len; the GEN worker builds a length-only placeholder and generation
   uses the KV transferred from the context worker (the forward path never
   dereferences prompt token VALUES, and seq_len/position come from prompt_len).
   GUARDRAIL: this fast path is taken only when the context worker reported a
   prompt_len AND the request does not read prompt token VALUES -- sampling
   penalties (frequency / presence / repetition) or prompt echo -- which the
   placeholder would silently corrupt; otherwise it falls back to relaying the
   real tokens. Deployment constraints: text-only, non-harmony, and GEN KV block
   reuse OFF (block-reuse keys blocks by token-value hash; a placeholder would
   collide across prompts and reuse the wrong KV).

2. int32-ndarray lazy submit path (correct for ALL requests). The GEN server
   keeps the base64-decoded prompt_token_ids as an int32 ndarray (drops the
   O(ISL) .tolist()); prompt_inputs gains a 1-D ndarray branch; LLM._preprocess
   guards prompt_token_ids with `is not None` (an ndarray truth value is
   ambiguous); executor.generate_async accepts np.integer; and GenerationRequest
   stashes the ndarray as the lazy `_prompt_token_ids_i32` buffer, memcpy'd into
   the C++ Request ctor. The Python list is built lazily only if a consumer reads
   the values (prompt logprobs); the plain disagg-gen path never does. Unlike the
   placeholder this carries the REAL tokens, so it is correct under all sampling
   features and composes with the base64 relay (NVIDIA#15698) and the ctor memcpy
   (NVIDIA#15211): CTX base64 -> orchestrator string relay -> GEN int32 ndarray (lazy)
   -> C++ memcpy, with no Python list built anywhere on the path.

Both opt-in (default off). Note: the int32 path does not remove the ISL request
body from the wire (only metadata-only does) and neither addresses the SSE
output-streaming load on the same event loop; the dominant "wait" (queuing) at
very high concurrency needs metadata-only body removal for all requests
(off-loop real-token delivery) and/or multiple front-end processes.

Tested: CPU unit tests (int32 laziness + real-value read + pickle round-trip;
prompt_inputs ndarray preservation; the metadata-only guardrail predicate).
Needs a GPU disagg smoke test for end-to-end TTFT/throughput/accuracy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yukun He <23156053+hyukn@users.noreply.github.com>
(cherry picked from commit 6da6e5b)
@hyukn

hyukn commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator Author

/bot run --disable-fail-fast

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59636 [ run ] triggered by Bot. Commit: 4f44592 Link to invocation

@tensorrt-cicd

Copy link
Copy Markdown
Collaborator

PR_Github #59636 [ run ] completed with state SUCCESS. Commit: 4f44592
/LLM/main/L0_MergeRequest_PR pipeline #48073 completed with status: 'FAILURE'

CI Report

⚠️ Action Required:

  • Please check the failed tests and fix your PR
  • If you cannot view the failures, ask the CI triggerer to share details
  • Once fixed, request an NVIDIA team member to trigger CI again

CI Agent Failure Analysis

Link to invocation

@hyukn hyukn closed this Jul 21, 2026
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.

2 participants