Skip to content

sglang rust server tokenizer and detokenizer - #32244

Closed
rainj-me wants to merge 1 commit into
rainj-me/rust-server-tm-samplingfrom
rainj-me/rust-server-tok-detok
Closed

rainj-me wants to merge 1 commit into
rainj-me/rust-server-tm-samplingfrom
rainj-me/rust-server-tok-detok

Conversation

@rainj-me

@rainj-me rainj-me commented Jul 23, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

Split PR from #29799

Depends on #32358

Modifications

Accuracy Tests

Speed Tests and Profiling

Checklist

Review and Merge Process

  1. Ping Merge Oncalls to start the process. See the PR Merge Process.
  2. Get approvals from CODEOWNERS and other reviewers.
  3. Trigger CI tests with comments or contact authorized users to do so.
    • Common commands include /tag-and-rerun-ci, /tag-run-ci-label, /rerun-failed-ci
  4. After green CI and required approvals, ask Merge Oncalls or people with Write permission to merge the PR.

CI States

Latest PR Test (Base): ✅ Run #30491901081
Latest PR Test (Extra): ❌ Run #30491900554

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions github-actions Bot added the dependencies Pull requests that update a dependency file label Jul 23, 2026
@rainj-me rainj-me mentioned this pull request Jul 23, 2026
13 of 30 tasks
@rainj-me
rainj-me force-pushed the rainj-me/rust-server-tok-detok branch from 1aaf596 to e23e6ec Compare July 23, 2026 20:52
@rainj-me rainj-me changed the title Rainj me/rust server tok detok sglang rust server tok detok Jul 23, 2026
@rainj-me rainj-me mentioned this pull request Jul 23, 2026
5 tasks done
@rainj-me
rainj-me force-pushed the rainj-me/rust-server-tok-detok branch 2 times, most recently from 6643d00 to c98e733 Compare July 25, 2026 00:44
@rainj-me rainj-me changed the title sglang rust server tok detok sglang rust server tokenizer and detokenizer Jul 25, 2026
@rainj-me rainj-me mentioned this pull request Jul 25, 2026
5 tasks done
@alexnails
alexnails changed the base branch from main to rainj-me/rust-server-tm-ingress July 26, 2026 20:09

@alexnails alexnails left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is most speculative review, will review again once egress and ingress PRs are in

for &id in token_ids {
if let Some(chunk) = self
.stream
.step(id as u32)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add vocab_size to the shard?

if stop.is_empty() {
return;
}
if let Some(pos) = text.find(stop) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given my other comment from earlier PR about FinalMetadata for multiplexing we should also have this adopt maybe a better form of code that is more end aware

/// Egress half of the lifecycle FSM. Lives here because the ingress
/// `Request` (and its FSM) was handed to the scheduler when queued; the
/// shard is the sole owner of the request's egress state, so no lock.
fsm: RequestState,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can discard Result in quite a few places

Comment thread rust/sglang-server/src/detokenizer.rs Outdated

impl Runnable for DetokenizerWorker {
fn run(self) {
let mut table: HashMap<RidHash, DetokState> = HashMap::new();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part 3 of hashing comment lol

[Agent Verification]

The lookup on line 237 runs once per ChunkEvent, so at batch 4096 over 8 shards that's ~512 lookups per shard per decode step. Measured (--release, 512 keys, 2000 iters):

per decode step
HashMap default (SipHash) — as written 9.8 µs
HashMap with a pass-through hasher 2.1 µs

4.7×, for a one-line type change:

type RidMap<V> = HashMap<RidHash, V, BuildHasherDefault<PassThroughHasher>>;

RidHash::from_rid already ran SipHash-1-3 over the rid string, so the key is well distributed by construction — the second hash adds no avalanche, only latency. A pass-through Hasher whose write_u64 stores the value is enough. (RidHash derives Hash on a u64 field, so it goes through write_u64 and there's no multi-field path to worry about.)

Two caveats worth stating rather than glossing:

  • HashDoS: pass-through hashing normally reopens that, but here the map key is already a keyed-hash digest, and sglang rust server tm egress #32249's ids.rs fixes the SipHash keys anyway — so the collision resistance of this map is already whatever RidHash provides, and re-hashing doesn't add any. Worth a comment saying so at the type alias.
  • Absolute size: ~8 µs per shard per step against a decode step of tens of ms is well under 1%. Same framing as my notes on the other two PRs — it's worth taking because it's free and this is the per-chunk path, not because it's a bottleneck today.

Related, while in run: this worker has no shutdown receiver and relies on rx closing, which per the comment requires the API runtime and tm-ingress and tm-egress to all release their Senders clones. But this worker holds tm: Sender<TmEvent> — a sender into the ingress inbox — and Ingress holds Senders (including detok), so the two keep each other's inboxes alive and neither can close by producer-drop. The chain only unwinds from Ingress's explicit shutdown signal outward. That works, but it's a four-party ordering whose only specification is this comment, spread across three PRs, and a single retained Senders clone anywhere hangs both pools forever with no timeout and no log. Worth either giving the pools the same shutdown receiver the TM stages have, or stating the invariant in one place.


/// Resolve a model file from the tokenizer source: a dir → `dir/<file>`, a file →
/// its sibling, else an HF Hub repo id → the local cache. `None` if not found.
pub fn resolve_model_file(path: &str, revision: Option<&str>, filename: &str) -> Option<String> {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests?

Comment thread rust/sglang-server/src/tokenizer.rs
// The tokenizer pool only ever receives generate requests. Encode,
// then advance the FSM: `TokenizeDone` on success.
let event = {
let RequestKind::Generate(g) = &mut req.kind else {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

copy Ingress::fail / ingress.rs behavior here?

@rainj-me
rainj-me force-pushed the rainj-me/rust-server-tm-ingress branch 3 times, most recently from 8a3a9e8 to f8a1aa9 Compare July 29, 2026 21:06
@rainj-me
rainj-me changed the base branch from rainj-me/rust-server-tm-ingress to rainj-me/rust-server-tm-sampling July 29, 2026 21:15
@rainj-me
rainj-me force-pushed the rainj-me/rust-server-tok-detok branch from c98e733 to 48413a4 Compare July 29, 2026 21:19
@rainj-me rainj-me closed this Aug 3, 2026
@Jiminator
Jiminator deleted the rainj-me/rust-server-tok-detok branch September 14, 2026 04:45
@alexnails
alexnails restored the rainj-me/rust-server-tok-detok branch September 14, 2026 05:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants