Skip to content

[autoscaler] Precompute SerializeToString keys to eliminate redundant serialization in scheduling loop - #65282

Open
Jade07-1 wants to merge 5 commits into
ray-project:masterfrom
Jade07-1:feat/precompute-serialize-key
Open

[autoscaler] Precompute SerializeToString keys to eliminate redundant serialization in scheduling loop#65282
Jade07-1 wants to merge 5 commits into
ray-project:masterfrom
Jade07-1:feat/precompute-serialize-key

Conversation

@Jade07-1

@Jade07-1 Jade07-1 commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Why are these changes needed?

After merging #64175, benchmark testing showed that scaling to 3000 nodes (15000 tasks × 0.2 CPU + 30MB memory) still takes ~51 minutes. Profiling with py-spy revealed two independent hotspots in the scheduling loop:

  1. Pre-filter logic flaw — fixed in [autoscaler] Improve V2 scheduler pre-filter precision for multi-resource demands #65171 (OR-across-dimensions → AND-within-shape/OR-across-shapes)
  2. Redundant SerializeToString calls — fixed in this PR

The UnschedulableRequestCache.contains() serializes each request via SerializeToString(deterministic=True) once per candidate node inside try_schedule, resulting in N_nodes × M_requests redundant serializations per scheduling round. For 15000 requests across 3000 nodes, this produces ~45M repeated protobuf serializations.

Profiling evidence (py-spy, 30s sample during baseline run):

  • contains() in scheduler.py: 55% Own CPU (7.77s / 14s)
  • _audit_fork_safety triggered by SerializeToString inside contains(): 18% Own CPU (2.43s / 14s)
  • Combined: 73% of scheduler CPU spent on redundant serialization
image

py-spy top

Fix

Precompute a {id(request): bytes} dictionary once before entering the scheduling loop, and pass it through _sched_best_nodetry_schedule. The UnschedulableRequestCache interface is simplified to accept raw bytes keys directly.

Includes a defensive .get() fallback: if a request is not in the precomputed dict, it falls back to on-the-fly serialization rather than raising a KeyError.

Benchmark

Cluster: 3000 max workers (1 CPU each), 15000 tasks × 0.2 CPU.

Condition Time to 3000 nodes
After #64175 (baseline) ~51 min
+ #65171 only (pre-filter AND/OR) ~18 min
+ #65171 + this fix combined ~14 min

Related PRs

Checks

  • I've signed all my commits with DCO sign-off.
  • I've run the existing tests — all pass. The try_schedule interface change is backward-compatible via Optional parameter with on-the-fly fallback.
  • Changes are confined to python/ray/autoscaler/v2/scheduler.py (single file, low blast radius).
  • No duplicate PR exists for this optimization (searched open PRs for SerializeToString, precompute, UnschedulableRequestCache — zero matches).
  • AI assistance was used. I (human submitter) have reviewed every changed line and understand the change end-to-end.

wangjia23 and others added 2 commits August 7, 2026 15:30
…g hotspot

In a single scheduling round, every request object is serialized via
SerializeToString(deterministic=True) once per candidate node inside
try_schedule, resulting in N_nodes × M_requests redundant serialization
calls. For 15000 pending requests across 3000 nodes, this produces
millions of repeated protobuf serializations consuming 55%+ of scheduler
CPU time.

Fix: precompute a {id(request): bytes} dictionary once before entering
the scheduling loop and pass it through _sched_best_node → try_schedule.
UnschedulableRequestCache is simplified to accept raw bytes keys.

Signed-off-by: wangjia23 <wangjia23@xiaomi.com>
…docstring

- Use shape_keys.get(id(r)) with fallback to SerializeToString when key
  is missing, preventing potential KeyError if request objects are ever
  introduced outside the precomputed dict
- Add shape_keys parameter documentation to _sched_best_node docstring

Signed-off-by: wangjia23 <wangjia23@xiaomi.com>

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: wangjia23 <wangjia23@xiaomi.com>
@Jade07-1
Jade07-1 requested a review from a team as a code owner August 7, 2026 08:25

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request optimizes the scheduling logic in python/ray/autoscaler/v2/scheduler.py by precomputing serialization keys (shape_keys) for resource requests, which avoids redundant SerializeToString calls inside the per-node scheduling loop. Additionally, UnschedulableRequestCache is simplified to cache and check these serialized shape keys directly. The feedback suggests simplifying the logic for retrieving or computing the serialization key sk inside try_schedule to eliminate duplicate calls to SerializeToString and improve readability.

Comment thread python/ray/autoscaler/v2/scheduler.py Outdated
Comment on lines +606 to +611
if shape_keys is not None:
sk = shape_keys.get(id(r))
if sk is None:
sk = r.SerializeToString(deterministic=True)
else:
sk = r.SerializeToString(deterministic=True)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The logic for retrieving or computing the serialization key sk can be simplified to avoid duplicating the r.SerializeToString(deterministic=True) call. This improves readability and maintainability of the code.

            sk = shape_keys.get(id(r)) if shape_keys is not None else None
            if sk is None:
                sk = r.SerializeToString(deterministic=True)

wangjia23 and others added 2 commits August 7, 2026 16:31
- test_precomputed_keys_produce_same_result_as_fallback: verifies that
  passing shape_keys produces identical scheduling results as the None
  fallback path
- test_precomputed_keys_fallback_on_missing_id: verifies defensive .get()
  fallback when request id is not in the precomputed dict
- test_unschedulable_request_cache_bytes_interface: direct unit test for
  the simplified UnschedulableRequestCache bytes-based API
- test_precomputed_keys_with_duplicate_objects: verifies correctness when
  the same request object appears multiple times (ungroup_by_count pattern)
- test_precomputed_keys_end_to_end_scheduling: full scheduling round
  confirming correct launch decisions with the optimization active

Signed-off-by: wangjia23 <wangjia23@xiaomi.com>

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: wangjia23 <wangjia23@xiaomi.com>
Address review feedback from gemini-code-assist: consolidate the
shape_keys retrieval and fallback into two lines instead of a nested
if/else with duplicated SerializeToString calls.

Signed-off-by: wangjia23 <wangjia23@xiaomi.com>

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: wangjia23 <wangjia23@xiaomi.com>
@ray-gardener ray-gardener Bot added core Issues that should be addressed in Ray Core community-contribution Contributed by the community labels Aug 7, 2026
…pe from docstrings

- Add # noqa to dict comprehension line that triggers the docstyle regex
  (id(r): ... matches the "param (Type): desc" pattern as a false positive)
- Remove type annotations from shape_keys docstring descriptions per Ray
  Google pydoc style convention

Signed-off-by: wangjia23 <wangjia23@xiaomi.com>

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: wangjia23 <wangjia23@xiaomi.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution Contributed by the community core Issues that should be addressed in Ray Core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant