Skip to content

perf(kv-router): parse selection requests without serde flatten or path tracking - #17238

Merged
jthomson04 merged 3 commits into
mainfrom
jthomson04/select-parse-perf
Oct 26, 2026
Merged

jthomson04 merged 3 commits into
mainfrom
jthomson04/select-parse-perf

Conversation

@jthomson04

@jthomson04 jthomson04 commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Overview:

Selection requests carry a whole prompt's token_ids. Parsing them was the largest cost in both the standalone selection service and the embedded SelectionService that Ray Serve LLM's KVAwareRouter calls. This PR removes the serde flatten buffering on the prompt, extracts token_ids with a typed pyo3 conversion in the binding, and parses HTTP bodies without serde_path_to_error.

A/B on one GB200 Grace node, same allocation. Setup: 1,024 mocker workers publishing real ZMQ KV events, 8,192 concurrent AgentX sessions with a 16k shared prompt (mean ISL 99k tokens), service pinned to 16 cores, 0 errors in every arm.

HTTP before HTTP after Embedded (Ray-style) before Embedded after
Requests/s 1,323 1,327 (engine-bound) 120 221 (+85%)
Service cores 15.1 12.0 (−21%) 1.5 (GIL thread 1.0) 2.0 (GIL thread 1.0)
CPU ms per request 11.4 9.1 12.4 9.1
Selection latency p50 / p99 6.5 / 25 ms 1.8 / 4.3 ms 65 s (queued on GIL) 34 s
Parse/convert, CPU ms per request 4.4 1.3 4.3 1.2
Share of CPU in serde Content buffering 38% 2.5% 40% 2%

Parse microbench, 100k-token /select body, local x86: 3.8 ms before, 1.55 ms without flatten, 0.82 ms with both changes.

Details:

  • services/selection/types.rs. SelectRequest, SelectAndReserveRequest, ReservationRequest, PotentialLoadsRequest and OverlapScoresRequest deserialize through flat wire structs (#[serde(from)], generated by inline_prompt_wire!) instead of #[serde(flatten)] prompt. The public structs and the wire fields are unchanged. flatten buffered the body into serde Content; under pythonize it also forced pyo3's slow 128-bit int extraction, about 48 ns per token. is_eagle still accepts only a boolean or null: a plain typed bool would let pythonize coerce truthy values such as "false".
  • lib/bindings/python/rust/llm/kv.rs. select, select_and_reserve, create_reservation, overlap_scores and potential_loads extract token_ids as a typed Vec<u32>, then depythonize the rest of the dict. Invalid tokens still raise ValueError.
  • services/selection/server.rs. The five prompt-carrying routes use a PromptJson<T> extractor: serde_json::from_slice, without axum Json's per-element path tracking. It keeps Json's rejections: 415 for a missing or non-JSON Content-Type, 400 for malformed JSON, 422 for a body of the wrong shape, and the body limit.

Where should the reviewer start?

services/selection/types.rs (inline_prompt_wire!), then PromptJson in server.rs.

Validation

  • cargo clippy -p dynamo-kv-router --features standalone-selection --all-targets -- -D warnings
  • cargo clippy --features select-service -- -D warnings in lib/bindings/python
  • cargo test -p dynamo-kv-router --features standalone-selection --lib services::: 251 passed, including the new prompt_carrying_requests_read_top_level_prompt_fields and prompt_routes_keep_json_rejection_statuses
  • pytest lib/bindings/python/tests/test_kv_bindings.py -k selection_service: 16 passed; the malformed-payload test now also covers "is_eagle": "false" (fails before the fix)

🤖 Generated with Claude Code

…th tracking

Selection requests carry a full prompt's token_ids. Three parse paths made
that the dominant cost of the selection service and of the embedded
SelectionService that Ray Serve LLM calls:

- Prompt-carrying request types embedded PromptRequest with
  #[serde(flatten)], which buffers the whole body through serde Content
  first; under pythonize it also forced pyo3's slow 128-bit int extraction
  for every token. They now deserialize through flat wire structs
  (serde(from)) with the same public shape and wire fields.
- The Python binding extracts token_ids with pyo3's typed Vec<u32>
  conversion before depythonizing the rest of the request.
- The five prompt-carrying HTTP routes use a PromptJson extractor:
  serde_json::from_slice without axum Json's serde_path_to_error
  per-element path tracking, keeping Json's 415/400/422 rejections.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: jthomson04 <jwillthomson19@gmail.com>
@jthomson04
jthomson04 requested review from a team as code owners October 24, 2026 05:00
@jthomson04

Copy link
Copy Markdown
Contributor Author

/ok to test b3ace9a

@github-actions github-actions Bot added the perf label Oct 24, 2026
@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 3032e8a and b3ace9a.

Files selected for processing: 4
  • lib/bindings/python/rust/llm/kv.rs
  • lib/kv-router/src/services/selection/server.rs
  • lib/kv-router/src/services/selection/tests.rs
  • lib/kv-router/src/services/selection/types.rs

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 11 remain after this review.

Walkthrough

Selection request deserialization now maps top-level prompt fields into nested prompts. Router endpoints use a custom JSON extractor with explicit content-type and parse-error statuses. Python bindings use a helper to process dictionary token_ids.

Changes

Selection prompt requests

Layer / File(s) Summary
Flat prompt request deserialization
lib/kv-router/src/services/selection/types.rs, lib/kv-router/src/services/selection/tests.rs
Wire structs deserialize top-level prompt fields into nested prompts and apply request defaults. Tests check prompt fields, defaults, and selected request values.
Router JSON extraction and rejection responses
lib/kv-router/src/services/selection/server.rs, lib/kv-router/src/services/selection/tests.rs
Selection endpoints use PromptJson. It accepts JSON and +json content types, returns 415 for missing or unsupported types, preserves body-extraction rejection details, maps data errors to 422, and maps other parse errors to 400. Tests check selected rejection responses.
Python prompt request deserialization
lib/bindings/python/rust/llm/kv.rs
A helper extracts non-None token_ids from dictionary inputs before request deserialization, then assigns them to the parsed prompt. Five request methods use the helper.

Priority: Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: Minimal · up to b3ace

No merge-blocking behavior change was identified; the selection request parsing changes are ready for normal validation.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 30.43% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 23 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly identifies the performance change: parsing selection requests without serde flattening or path tracking.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description check Passed The description is thorough and mostly complete. It has an Overview with the goal (removing serde flatten buffering, typed token_ids extraction, and HTTP parsing without serde_path_to_error), a Detail…
Full details: Description check

Explanation

The description is thorough and mostly complete. It has an Overview with the goal (removing serde flatten buffering, typed token_ids extraction, and HTTP parsing without serde_path_to_error), a Details section that lists the changed files and the reasoning, a Where should the reviewer start section, and a Validation section with commands and results. Benchmark data supports the claims. The one required gap is the Related Issues section. It still holds the template placeholder 'Closes #XXXX' and does not name a real issue, although the objectives cite PR #17238 and the template marks this section as required. The check passes on overall completeness, but the Related Issues section needs a real reference before merge.

Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@devin-ai-integration

devin-ai-integration Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

✅ Dynamo PR CI passed — run 40131064575 (attempt 1) on 654d716824

Gate checks: ✅ backend-status-check · ✅ deploy-status-check · ✅ dynamo-status-check

Posted automatically by Devin for run 40131064575. Updated on every full-CI run of this PR.

jthomson04 and others added 2 commits October 24, 2026 13:38
Without flatten, the wire struct's typed bool went through pythonize's
deserialize_bool, which reads Python truthiness: "is_eagle": "false"
enabled Eagle hashing. Accept only a boolean or null, as the flattened
path did. HTTP is unchanged; serde_json's bool was already strict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: jthomson04 <jwillthomson19@gmail.com>
- Binding: one depythonize_prompt_request taking a prompt accessor
  replaces the PromptRequestBody trait, its impl macro and module.
- has_json_content_type matches the +json suffix without allocating.
- types.rs: the wire macro and strict_bool sit next to the request
  types that use them; BlockExtraInfo joins the crate::protocols import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: jthomson04 <jwillthomson19@gmail.com>
@jthomson04

Copy link
Copy Markdown
Contributor Author

/ok to test 654d716

@jthomson04
jthomson04 merged commit 2d17836 into main Oct 26, 2026
129 of 130 checks passed
@jthomson04
jthomson04 deleted the jthomson04/select-parse-perf branch October 26, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants