Repository navigation
[sampler cold start] Keep sampler kernels on the shared kernel-cache volume - #146
Merged
Merged
Conversation
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>
This was referenced Oct 28, 2026
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>
…-kernel-cache # Conflicts: # CHANGELOG.md
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Samplers now keep their compiled kernels on the shared
spindle-kernel-cachevolume, 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 ininference_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) ondeepseek_v41_flash_1017_lora_8kwith 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 / AfterKL 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
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