Back to Deep Dives
Technical Deep Dive

Cold Start Engineering

How GPU memory snapshots, CRIU checkpointing, JIT kernel caching, and volume symlinks cut cold starts from 26 minutes to 7 minutes on Modal B200 clusters. Production-verified across vLLM, SGLang, and llama.cpp.

GPU Infrastructure·5 min read·Production Verified

How do you reduce GPU cold start times for LLM inference in production?

GPU memory snapshots (CRIU) are the highest-leverage optimization, delivering 5×–24× cold start reductions across diffusers, llama.cpp, and SGLang. In-process model loading is mandatory when child CUDA contexts are invisible to snapshots; llama-server subprocess trees can work with --no-mmap. Verified on Modal L40S and B200 GPUs, Feb–May 2026.

TL;DR Summary

  • Highest-Leverage Optimization: GPU memory snapshots are the most impactful optimization, delivering 5× to 24× cold start reductions across diffusers, llama.cpp, and SGLang.
  • In-process Loading Mandatory: Capturing full GPU state with CRIU requires in-process model loading; child processes (e.g. subprocess.Popen) are invisible to the snapshot system.
  • SGLang & llama-server Hacks: SGLang requires --enable-memory-saver to offload KV cache to CPU during capture. Llama-server requires --no-mmap for checkpointing compatibility.
  • Warmup Requests: Running warmup queries before snapshotting forces CUDA JIT kernel compilation, keeping it out of the cold start path.

GPU memory snapshots are the single most impactful optimization, dominating all other cold start improvements combined. Across 4+ different inference engines and model architectures, snapshots deliver 5× to 24× cold start reductions.

How much can GPU memory snapshots improve cold start times?

Across four engines and models, snapshots deliver 5× to 24× cold start reductions, from 48s to ~7s on FLUX.2 and 110s to 2–7s on GLM-4.7-Flash GGUF.

ModelEngineBeforeAfterImprovement
FLUX.2-klein-9Bdiffusers48s~7s6.9×
GLM-4.7-Flash GGUFllama-cpp-python110-168s2-7s24×
Gemma 4 26B GGUFllama-server60-120s5-15s5-10×
Qwen3.5-35B-A3B FP8SGLang2-5 min12-20s~10×

What dominates cold start time before snapshots?

CPU→GPU weight transfer dominates (35s of a 48.3s FLUX.2 baseline), and snapshots cut that phase to ~3s. The baseline for FLUX.2-klein-9B was 48.3s cold start. CPU→GPU transfer of ~18 GB bf16 weights dominated with 35s of overhead. Enabling GPU memory snapshots captured full CUDA state: containers restored in ~3s, dropping overhead from 35.0s to 3.1s (11× less).

Marginal improvements (pre-importing libraries, reducing wait_ms, baking weights into image) combined saved another few seconds, but the snapshots did 90%+ of the work.

Why do subprocess-based model servers break GPU snapshots?

CRIU cannot capture child-process CUDA contexts started via subprocess.Popen. In-process model loading is mandatory for snapshot capture. The GLM-4.7-Flash GGUF deployment initially used subprocess.Popen to run llama.cpp server. Cold starts took 110-168s because Modal's GPU snapshot system (CRIU) cannot capture child process GPU state.

Switching to in-process Llama(...) loading so Modal captures the entire Python process including its CUDA context cut cold starts to 13-33s. Further optimizations (runtime image, pre-built wheels, single warmup) brought it to 2-7s.

How do SGLang memory-saver hooks enable GPU snapshots?

Call /release_memory_occupation before capture and /resume_memory_occupation after restore, with --enable-memory-saver and TORCHINDUCTOR_COMPILE_THREADS=1. For SGLang deployments (Qwen3.5-35B), the snapshot path uses SGLang's built-in hooks: the server calls /release_memory_occupation to offload KV cache to CPU, Modal captures the full container state including GPU memory, and on restore the server calls /resume_memory_occupation.

This requires --enable-memory-saver and --enable-weights-cpu-backup flags plus TORCHINDUCTOR_COMPILE_THREADS=1 to prevent OOM during snapshot creation.

When can llama-server subprocess trees still use GPU snapshots?

When CRIU captures the full process tree, snapshots work if --no-mmap is set so memory-mapped weights do not block checkpointing. For Gemma 4 running llama-server (subprocess), snapshots work because CRIU captures the entire process tree. The critical requirement is --no-mmap: memory-mapped model files prevent CRIU from properly checkpointing the GPU memory state.

What are the key cold start optimization lessons?

GPU snapshots are the highest-leverage optimization; everything else is marginal. Warmup before snapshot, in-process loading, and scaledown_window are the supporting tactics.

  1. GPU snapshots are the single highest-leverage optimization: everything else is marginal. The most impactful action across all deployments was enabling GPU memory snapshots.
  2. In-process loading is mandatory for snapshot capture: child processes started via subprocess are invisible to CRIU.
  3. Warmup before snapshot: CUDA kernel JIT compilation happens on first inference. Run warmup requests with varied sequence lengths before the snapshot is taken.
  4. Snapshot rebuild is a one-time cost per deploy: first request after deploy takes 60-190s to rebuild. Subsequent requests use the cached snapshot.
  5. scaledown_window is the first line of defence: keeping containers alive for 5 minutes after last request avoids most cold starts entirely. Costs ~$0.002/min for idle L40S.
  6. Runtime images beat devel images: using nvidia/cuda:12.4.1-runtime-ubuntu22.04 instead of the devel variant saves ~1.5 GB and eliminates build toolchain dependencies.
  7. --no-mmap is required for CRIU compatibility with llama.cpp-based deployments.
  8. Baking model weights into the image is a tradeoff: beneficial under ~15-20 GB, counter-productive above ~25 GB.

What errors block GPU snapshot deployments?

SIGSEGV on restore, invisible child CUDA contexts, OOM during snapshot creation, and mmap-incompatible weight loading are the most common blockers. Each has a documented fix below.

ErrorRoot CauseResolution
SIGSEGV on restoreGPU snapshot CUDA handle incompatibilityUpdated Modal API; no recurrence
Child process GPU state not capturedsubprocess.Popen invisible to CRIURewrote to in-process model loading
OOM during snapshot creationTorchInductor thread pool competing for VRAMSet TORCHINDUCTOR_COMPILE_THREADS=1
libgomp.so.1 not foundRuntime image missing OpenMPAdded apt_install("libgomp1")
SSL certificate verification failureLocal testing without valid certificatesCreated _make_ssl_ctx() for local dev

Source: §1 (FLUX.2-klein-9B), §3 (GLM-4.7-Flash GGUF), §5 (Gemma 4 26B GGUF), §7 (Qwen3.5-35B-A3B FP8).

Frequently Asked Questions

What is the most effective way to reduce GPU cold start times for LLM inference?
GPU memory snapshots (via CRIU) are the single highest-leverage optimization. Across vLLM, SGLang, diffusers, and llama.cpp deployments, snapshots deliver 5× to 24× cold start reductions. In-process model loading is required when child processes started via subprocess.Popen create CUDA contexts invisible to the parent snapshot.
How do GPU memory snapshots work for LLM cold start reduction?
GPU memory snapshots capture the full CUDA state of a running container using CRIU. On restore, the container resumes from snapshot instead of reloading weights from disk. For FLUX.2-klein-9B on Modal L40S GPUs, CPU-to-GPU transfer dropped from 35s to 3s, achieving 6.9× total cold start reduction (48s to 7s).
How do you cache FlashInfer JIT kernels to avoid long boot times on large models?
For Qwen3.5-397B on Modal 4× B200 GPUs, FlashInfer JIT compilation added significant cold-start overhead. Caching compiled kernels to a persistent volume and symlinking at boot reduced cold start from 26 minutes to 7 minutes, unlocking scale-to-zero and saving ~74% on GPU costs.
When can subprocess-based model servers still use GPU snapshots?
When CRIU captures the entire process tree (e.g. llama-server as subprocess), snapshots can work if --no-mmap is set so memory-mapped weights do not block checkpointing. Popen-launched children whose CUDA context is invisible to the parent process cannot be snapshotted; use in-process loading instead.

Related deep dives

About the author

Yuvraj Garg

AI Systems & Infrastructure Architect

Yuvraj Garg is an AI Systems and Infrastructure Architect specializing in production GPU serving, multi-agent orchestration, and ML cost optimization. He architected REimagineHome.AI (2.1M+ users) and has documented 18.6× cost reductions and 24× cold start improvements across self-hosted LLM infrastructure.