Skip to content

perf(serverless): 8x64 MiB VolumeCache hydrate - #691

Open
deanq wants to merge 1 commit into
mainfrom
deanq/sls-990-volume-cache-hydrate-default
Open

deanq wants to merge 1 commit into
mainfrom
deanq/sls-990-volume-cache-hydrate-default

Conversation

@deanq

@deanq deanq commented Nov 6, 2026 •

Copy link
Copy Markdown
Member

Summary

Change VolumeCache's default hydrate setting from 64 workers × 8 MiB to 8 workers × 64 MiB, keeping the same 512 MiB in flight. The default chunk is now 512 MiB / hydrate_workers, capped at 64 MiB, so callers who set max_workers or hydrate_workers stay within the budget.

Why

The 64 × 8 MiB default from #621 was tuned for Global Store only. On a network volume it's 2–2.8× slower than 8 × 64 MiB, and it made VolumeCache slower than loading the model in place: in flash-model-cache-bench (Qwen2.5-3B, 4090, EU-RO-1), VolumeCache took 23.1 s against 21.2 s in place. On Flash 1.20 with whole-file copies it took 7.2 s.

Evidence

Hydrate throughput in MiB/s, median [range]. Stock runpod==2.1.0 with explicit settings, page cache evicted before each run, both settings run in alternating order each rep.

Qwen2.5-7B (14.2 GiB, 25 files)

Arm 64 × 8 (old) 8 × 64 (new) ratio
Network volume, 4090 pod, EU-RO-1 (n=4) 729 [708–745] 1416 [1385–1430] 1.94×
Global Store, 4090 pod, EU-RO-1 (n=4) 823 [291–904] 914 [821–971] 1.11×
Global Store, 1×H100 on an 8-GPU node, EU-FR-1 (n=3) 723 [518–732] 645 [636–814] 0.89×
Global Store, Serverless forced-cold, 4090 (n=4) 514 [334–697] 419 [202–811] 0.82×

Qwen2.5-3B (5.76 GiB, 21 files)

Arm 64 × 8 (old) 8 × 64 (new)
Network volume, Serverless, 1 worker (n=2) ~360 ~1000
Global Store, 4090 pod (n=6) 539 [110–596] 619 [149–682]
Global Store, Serverless (n=6) 346 [138–645] 348 [141–539]

Network volume: the ranges don't overlap. Global Store: the direction flips between arms and every range overlaps, so the difference is within run-to-run noise.

Retries and completeness: across all 44 hydrates in the 7B run, there were zero ENOMEM/EIO/EAGAIN retries and no incomplete hydrates. That covers the 8-GPU host where an ENOMEM storm was previously seen at more than 512 MiB in flight.

This branch's default path: I swapped this branch's code into the pod and ran 2 reps per volume with no explicit settings. The default resolved to 8 × 64 MiB, and the results matched the explicit numbers: network volume 1443 [1422–1463], Global Store 658 [349–967].

Caveat: the first hydrate on a fresh Serverless worker reading Global Store is slow whatever the setting (~150–350 MiB/s, cold host geesefs). Pooled cold first-hydrates hint 8 × 64 may be about 20% slower there (262 vs 332 MiB/s, n=4 each), but that isn't conclusive. The docs suggest trying hydrate_workers=64 on Global Store if that matters.

Changes

  • rp_volume_cache.py: _HYDRATE_WORKERS = 8, plus _HYDRATE_BYTES_IN_FLIGHT = 512 MiB and _HYDRATE_MAX_CHUNK_SIZE = 64 MiB. The default chunk comes from _default_chunk_size(workers), and an explicit hydrate_chunk_size is never rescaled. Docstring updated.
  • docs/serverless/volume_cache.md: defaults table and the hydrate section.
  • Tests: the default values, chunk scaling for 4/16/32/64 workers (bytes in flight ≤ 512 MiB), max_workers=32 → 16 MiB, and explicit chunk sizes left unchanged.

Test plan

  • make quality-check: 2755 passed, coverage 94.27%
  • Live measurements on Runpod (tables above)

The 64 x 8 MiB default was tuned for Global Store only. On a network
volume 8 x 64 MiB at the same 512 MiB in flight hydrates 2-2.8x faster:
Qwen2.5-7B ~1420 vs ~730 MiB/s (4090 pod, n=4, ranges disjoint),
Qwen2.5-3B ~1000 vs ~360 MiB/s. With the old default VolumeCache was
slower than loading in place (3B: 23.1 s vs 21.2 s).

On Global Store the two are within run-to-run noise (7B: pod 914 vs 823,
8-GPU H100 host 645 vs 723, Serverless 419 vs 514 MiB/s, all ranges
overlapping) with zero ENOMEM/EIO/EAGAIN retries and no incomplete
hydrates across 44 runs.

The default chunk is now 512 MiB / hydrate_workers, capped at 64 MiB, so
callers who set max_workers or hydrate_workers stay within the
bytes-in-flight budget instead of reaching the ENOMEM zone (e.g.
max_workers=32 gets 16 MiB ranges rather than 2 GiB in flight).
@deanq
deanq requested a balanced review from Copilot November 6, 2026 21:24
@deanq
deanq requested review from a team, KAJdev and murat-runpod November 6, 2026 21:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟢 Approval recommended

No active review issues remain for the VolumeCache hydration tuning changes.

No open issues 🎉

What changed in this PR

This PR tunes VolumeCache hydration for faster network-volume reads, changing the default from 64 workers × 8 MiB to 8 workers × 64 MiB while retaining 512 MiB in flight.

Changes:

  • Derives default chunk size from the worker count, capped at 64 MiB, while preserving explicit chunk sizes.
  • Adds tests for the new defaults, worker-dependent scaling, and explicit overrides.
  • Updates documentation with the new defaults and Global Store tuning guidance.
File Description
tests/​test_serverless/​test_utils/​test_rp_volume_cache.py Tests defaults, chunk scaling, and explicit overrides.
runpod/​serverless/​utils/​rp_volume_cache.py Changes hydrate defaults and calculates worker-dependent chunk sizes.
docs/​serverless/​volume_cache.md Documents defaults, memory budgeting, and Global Store tuning.

Review effort: Balanced

@briandconnelly briandconnelly left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found.

No actionable defects were found in the reviewed scope. Tests were assessed by reading them, not by running them.

What changed

The pull request changes VolumeCache’s hydrate defaults to 8 workers with 64 MiB ranges and scales the default range size to keep configured pools within a 512 MiB budget. It updates documentation and adds tests for worker-dependent sizing and explicit chunk-size overrides.

👾 Reviewed by review-agent (GPT-6.1 Sol) on behalf of briandconnelly

Commit b2d4d38 · 0 inline comments

This branch has not been deployed

No deployments
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.

3 participants