Skip to content

[sampler cold start] Keep sampler kernels on the shared kernel-cache volume - #146

Merged
micahtyong merged 7 commits into
mainfrom
micah/sampler-perf/S1-kernel-cache
Oct 28, 2026
Merged

micahtyong merged 7 commits into
mainfrom
micah/sampler-perf/S1-kernel-cache

Conversation

@micahtyong

@micahtyong micahtyong commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Samplers now keep their compiled kernels on the shared spindle-kernel-cache volume, the way trainers already do (#82). Before, every sampler start compiled its kernels again on the container's local disk. For DeepSeek-V4.1-Flash that took about five minutes of a sixteen-minute start.

SGLang keeps all of its compile caches under one folder, set by SGLANG_CACHE_DIR. Spindle now points that folder at the volume for samplers. A recipe can still override it in inference_env. Trainers don't get it: the trainer imports SGLang too, and the setting would move its CUDA cache onto the network volume.

SGLang also looks in that folder for a small Rust extension it uses to index cached prompts. When it doesn't find it there, it compiles it with cargo at startup. The sampler image now ships the extension prebuilt and tells SGLang never to build it.

The compiled kernels in the cache are keyed by library version and GPU architecture, so H200 and Blackwell samplers can share the volume.

This branch also carries #147, which was merged into it. DeepSeek-V4.1-Flash samplers now run up to 128 requests at once instead of 32, so the 128 samples of a GRPO step generate together instead of in four waves. At 128 requests, an SGLang optimization that splits decode work across several CUDA streams crashed on Blackwell while the sampler recorded its decode graphs. The recipe turns that optimization off. H200 uses less of it and never crashed.

E2E results

Setup: stock tinker-cookbook math_rl (GSM8K) on deepseek_v41_flash_1017_lora_8k with one 8×H200 sampler replica. Before is main. After is the whole stack (#146, #148, #149) with this model's kernels already on the volume. Before / After

Before After
Kernel compile at sampler start 304 s 13 s
Sampler start to ready 985 s 438 s
Sampling time per GRPO step 42.3 s 22.9 s

KL between sampler and trainer stayed flat (0.0032 before, 0.0024 after).

The 128-request change on its own, on one sampler with 128 requests sent at once (16 GSM8K prompts × 8 samples, up to 512 tokens). Before / After

Per round of 128 samples Before After
Time until all 128 finish 37 s 19.7 s
Mean time per sample 32.8 s 11.7 s

With the multi-stream optimization off, an 8×B300 sampler recorded its decode graphs up to batch 128 and served three rounds of 128 requests with no errors. With it on, B200 and B300 samplers crashed at that step. On H200 the setting makes no measurable difference: 866 tok/s with it off and 881 tok/s with it on, within run-to-run noise.

🤖 Generated with Claude Code

micahtyong and others added 2 commits October 27, 2026 16:10
Samplers mounted the kernel-cache volume but never pointed SGLang at it, so
every replica recompiled its JIT kernels on start. SGLang redirects all of its
caches under SGLANG_CACHE_DIR, so the sampler gets that one variable and keeps
its caches apart from the trainer's.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
With the cache directory on the volume, SGLang's loader looked for the
fork's radix-tree binding there and built it with cargo on the first start.
Build it once in the image, keep it next to its Python module, and load it in
never mode so no sampler builds Rust at startup.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
micahtyong and others added 2 commits October 27, 2026 19:31
Drop the cargo cache from the image once the binding is bundled, test that a
recipe's inference_env overrides the default cache directory, and shorten the
comments.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
micahtyong and others added 3 commits October 28, 2026 10:32
…s at once (#147)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… samplers

With 128 running requests, samplers on B200 and B300 died while capturing
the batch-128 decode CUDA graph. The same capture passes with kernel launches
serialized, and passes with the Blackwell-only multi-stream decode overlap
turned off, so the overlap races between streams at large batches. H200
never takes that path. Off until the fork carries the stream dependency.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@micahtyong
micahtyong added this pull request to stack #166 October 28, 2026 23:31
@micahtyong
micahtyong merged commit d67ca93 into main Oct 28, 2026
4 checks passed
@micahtyong
micahtyong deleted the micah/sampler-perf/S1-kernel-cache branch October 28, 2026 23:45
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.

1 participant