Skip to content

[Feature] Support --tokenizer-worker-num > 1 in the offline Engine API - #40260

Merged
ishandhanani merged 3 commits into
sgl-project:mainfrom
peilii:peili/engine-multi-tokenizer
Sep 22, 2026
Merged

ishandhanani merged 3 commits into
sgl-project:mainfrom
peilii:peili/engine-multi-tokenizer

Conversation

@peilii

@peilii peilii commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

With tokenizer_worker_num > 1, Engine.tokenizer_manager is a MultiTokenizerRouter, which has no generate_request, so Engine.generate() / async_generate() fail on the first call (#15157). The router only bridges TokenizerWorkers to the scheduler. The HTTP server side-steps this by running each uvicorn worker as a TokenizerWorker initialized from shared memory, but the offline Engine API has no equivalent, so embedders such as Dynamo (ai-dynamo/dynamo#15078) cannot spread the per-request Python work of one engine over several processes.

Modifications

Give the Engine API the same shape as the HTTP server:

  • Engine.__init__: when tokenizer_worker_num > 1, publish (port_args, server_args, scheduler_info) to shared memory via the existing write_data_for_multi_tokenizer, and create an in-process TokenizerWorker so the parent Engine's generate() keeps working. The router is kept as Engine.tokenizer_router.
  • Engine.attach_tokenizer_worker(parent_pid=None) (new classmethod): build an Engine in another process that owns only a TokenizerWorker registered with the parent's router. generate() runs there while prefill/decode stay on the parent's schedulers.
  • Engine.shutdown(): attached engines only close their worker; the parent unlinks the shared memory.

No behavior change for tokenizer_worker_num == 1.

Fixes #15157

Accuracy Tests

Not applicable: no change to model forward or kernels. The new test checks that an attached process returns the same text and token counts as the parent for greedy decoding.

Speed Tests and Profiling

Not applicable to single-process use; the change is opt-in via tokenizer_worker_num.

Checklist

🤖 Generated with Claude Code


CI States

Latest PR Test (Base): ✅ Run #35658106558
Latest PR Test (Extra): ❌ Run #35658106054
Latest PR Test (AMD ROCm 10): ❌ Run #35658106548

@ishandhanani

Copy link
Copy Markdown
Collaborator

/tag-and-rerun-ci

@github-actions github-actions Bot added the run-ci CI: run the baseline test suite on this PR label Sep 18, 2026
@ishandhanani
ishandhanani marked this pull request as ready for review September 18, 2026 23:57
@peilii
peilii force-pushed the peili/engine-multi-tokenizer branch from f8308d0 to 3c3b938 Compare September 21, 2026 18:38
With tokenizer_worker_num > 1, Engine.tokenizer_manager was a MultiTokenizerRouter,
which has no generate_request, so Engine.generate()/async_generate() failed at the
first call (sgl-project#15157). The router only bridges TokenizerWorkers to the scheduler; the
HTTP server side-steps this by running each uvicorn worker as a TokenizerWorker
initialized from shared memory.

Give the Engine API the same shape:
- Engine.__init__ publishes (port_args, server_args, scheduler_info) to shared memory
  (write_data_for_multi_tokenizer) and creates an in-process TokenizerWorker, so the
  parent Engine keeps working; the router is kept as Engine.tokenizer_router.
- Engine.attach_tokenizer_worker(parent_pid=None) builds an Engine in another process
  that owns only a TokenizerWorker registered with the parent's router: generate()
  runs there while prefill/decode stay on the parent's schedulers. This lets callers
  spread the per-request Python work of one engine over several processes.
- shutdown(): attached engines only close their worker; the parent unlinks the shm.

Fixes sgl-project#15157
…gisters

TokenizerWorker.__init__ sends its registration to the router over a
zmq.asyncio socket, which schedules on the current event loop. Engine
only created its loop after the tokenizer worker, so a plain
sgl.Engine(tokenizer_worker_num=2) from a thread without a loop (the CI
test) failed with 'There is no current event loop'. Establish the loop
first, on both the constructor and attach_tokenizer_worker paths, and use
the same loop object for generate().
@peilii
peilii force-pushed the peili/engine-multi-tokenizer branch from 3c3b938 to 8c317fa Compare September 21, 2026 21:35
@ishandhanani

Copy link
Copy Markdown
Collaborator
Screenshot 2026-09-22 at 10 49 37 AM

@ishandhanani
ishandhanani merged commit 66454d7 into sgl-project:main Sep 22, 2026
158 of 174 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-ci CI: run the baseline test suite on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] --tokenizer-worker-num parameter is unsupported in offline engine

2 participants