Decoding the GPU Memory Puzzle: How a Deep-Dive Into SGLang's KV Cache Allocation Formula Uncovered Hidden Headroom

Introduction

In the course of deploying a massive 547 GB Kimi-K2.5 INT4 model across eight NVIDIA RTX PRO 6000 Blackwell GPUs, the assistant received a seemingly simple question from the user: "seems like I'm kinda low on max parallel request context, what options do we have for ram offload of kv cache?" (see [msg 5720]). What followed was a forensic investigation into SGLang's memory allocation logic that spanned multiple tool calls, source code spelunking, and a subagent task — culminating in message [msg 5736], where the assistant laid bare the precise formula governing KV cache sizing and presented a menu of optimization options. This article examines that message in depth: the detective work that led to it, the assumptions it challenged, the knowledge it synthesized, and the strategic fork it presented to the user.

Context: The Production Deployment of Kimi-K2.5

By the time message [msg 5736] was written, the assistant and user had already navigated a long and winding journey. The Kimi-K2.5 INT4 model had been deployed with EAGLE-3 speculative decoding using a top-k of 1 and spec_v2 overlap scheduling — a configuration that finally matched or beat baseline throughput at high concurrency after weeks of tuning. The deployment had been codified into a systemd service (sglang-kimi.service) with auto-start on boot. Critical operational fixes had been applied, including adding --tool-call-parser kimi_k2 and --reasoning-parser kimi_k2 to properly structure tool calls and reasoning content in API output.

But the user noticed a constraint: the maximum parallel request context seemed limited. The KV cache — the memory structure that stores key-value attention vectors for each token in the active context — was capped at approximately 159,277 tokens per GPU, consuming 10.42 GB. With 96 GB GPUs, this seemed wasteful. The user's question about RAM offloading was the spark that ignited a deep investigation.

The Investigation Preceding the Message

Before message [msg 5736], the assistant executed a multi-pronged investigation spanning messages [msg 5721] through [msg 5735]:

  1. Hardware inventory ([msg 5721]): nvidia-smi showed 92 GB used out of 96 GB per GPU, with only ~5 GB free. System RAM showed 396 GB available out of 449 GB — plenty of headroom for CPU offloading.
  2. Journal log analysis ([msg 5721], [msg 5722]): The assistant pulled KV cache allocation logs showing "KV Cache is allocated. #tokens: 159277, KV size: 10.42 GB" and "Memory pool end. avail mem=7.90 GB" — revealing significant free memory after allocation.
  3. Subagent research ([msg 5723]): A task subagent was dispatched to research all KV cache offloading options in SGLang v0.5.9, returning a comprehensive summary of hierarchical cache, FP8 KV cache, and other features.
  4. Source code analysis ([msg 5725][msg 5735]): The assistant traced the memory math step by step, then dove into SGLang's Python source code — reading model_runner_kv_cache_mixin.py, tp_worker.py, and model_runner.py — to find the profile_max_num_token function and understand how max_total_num_tokens is computed. This was not a superficial glance. The assistant read specific line ranges from the source files, searching for variable names like rest_memory, available_size, mem_fraction_static, and max_total_num_tokens. It traced the allocation logic from the high-level server arguments down to the per-GPU memory pool initialization.

The Subject Message: A Forensic Accounting of GPU Memory

Message [msg 5736] opens with the key discovery:

Now I see the key formula. rest_memory = available_gpu_memory - total_gpu_memory * (1 - mem_fraction_static):

>

- available_gpu_memory = 21.71 GB (after target model weights loaded) - total_gpu_memory = ~95.6 GB (96 GB total) - 1 - 0.88 = 0.12 - rest_memory = 21.71 - 95.6 * 0.12 = 21.71 - 11.47 = 10.24 GB

>

That matches — 10.42 GB KV cache. The formula reserves 12% of total GPU memory as headroom regardless of actual usage. The 7.43 GB left over is from that 12% reserve (11.47 GB) minus draft model weights (0.95 GB), draft KV (0.60 GB), and cuda graphs (0.37 GB + more for target graphs).

This was the "aha" moment. The assistant had reverse-engineered SGLang's memory allocation formula by reading the source code and plugging in the actual numbers from the deployment. The formula revealed a subtle but critical behavior: the mem_fraction_static parameter doesn't simply reserve a fraction of available memory for KV cache. Instead, it reserves (1 - mem_fraction_static) of the total GPU memory as a fixed headroom buffer, regardless of how much memory the model weights actually consume.

This means that with mem_fraction_static=0.88, SGLang reserves 12% of 96 GB = 11.47 GB as overhead headroom. But the model weights only consume 72.33 GB, leaving 21.71 GB available. After subtracting the 11.47 GB headroom, only ~10.24 GB remains for KV cache — and after also accounting for draft model weights (0.95 GB), draft KV cache (0.60 GB), and CUDA graph memory (~0.37+ GB), the remaining ~7.43 GB sits unused as "safety margin."

The Thinking Process Revealed

The message reveals a meticulous, step-by-step reasoning process. The assistant:

  1. Identified the formula by reading the profile_max_num_token method in SGLang's source code.
  2. Validated the formula against observed behavior: plugging in 21.71 GB available, 95.6 GB total, and 0.88 mem_fraction yields 10.24 GB — closely matching the observed 10.42 GB KV cache allocation.
  3. Traced the residual headroom: The 7.43 GB of "wasted" space wasn't truly wasted — it was the 11.47 GB headroom reserve (12% of total) being partially consumed by draft model weights, draft KV cache, and CUDA graph captures.
  4. Evaluated the trade-offs: Rather than jumping to a recommendation, the assistant systematically laid out the four options with their respective costs and benefits.
  5. Ended with a diagnostic question: "What's your actual bottleneck — running out of KV space with many concurrent long-context requests, or prefix cache misses on shared contexts?" This shows the assistant understood that the right solution depends on the specific workload characteristic.

Input Knowledge Required

To fully understand this message, the reader needs knowledge across several domains:

SGLang architecture: Understanding that SGLang uses tensor parallelism (TP8 across 8 GPUs), that KV cache is allocated per-GPU, and that the mem_fraction_static parameter controls the memory allocation trade-off between KV cache and headroom for activations.

GPU memory model: Knowledge that GPU memory is shared between model weights, KV cache, activations, CUDA graphs, and temporary buffers. The concept of "available GPU memory" after weight loading is distinct from "total GPU memory."

KV cache fundamentals: Understanding that the KV cache stores key-value attention vectors for each token position, and its size scales with sequence length, batch size, number of layers, and hidden dimension. The cache is typically allocated as a fixed-size pool during server startup.

Speculative decoding architecture: The deployment uses EAGLE-3 with a draft model, which means there are two sets of weights and two KV caches — target and draft — competing for the same GPU memory.

CUDA graphs: The message mentions "cuda graphs" consuming memory. CUDA graphs are pre-optimized execution schedules that capture GPU kernel launches for repeated computation patterns, reducing launch overhead. They consume GPU memory for the captured graph structures.

FP8 quantization: The option to use FP8 KV cache dtype requires understanding that reducing KV cache precision from bf16 (2 bytes per value) to fp8 (1 byte per value) halves memory usage, with minimal accuracy impact on attention computations.

Output Knowledge Created

This message creates several layers of valuable knowledge:

1. A documented reverse-engineering of SGLang's memory formula: The explicit formula rest_memory = available_gpu_memory - total_gpu_memory * (1 - mem_fraction_static) is a critical piece of systems knowledge that was not obvious from SGLang's documentation. The assistant extracted it from source code and validated it against real measurements.

2. A concrete memory budget breakdown per GPU: The message provides a line-item accounting of how the 96 GB GPU is consumed:

Assumptions and Potential Pitfalls

The message makes several assumptions worth examining:

Assumption that mem_fraction_static=0.88 is already aggressive: The assistant states "0.88 is already aggressive" but doesn't quantify the risk of going higher. In practice, the aggressiveness depends on the workload — a server with small batch sizes and short sequences might safely use 0.92, while one with large batches and long sequences might OOM at 0.90.

Assumption that FP8 KV cache has "minimal quality impact": While generally true, this depends on the model and task. Some models are more sensitive to KV cache precision loss, particularly at long contexts or for tasks requiring fine-grained attention patterns. The assistant correctly notes that the user "rejected FP8 KV earlier" but doesn't revisit that decision in depth.

Assumption about PCIe bandwidth for hierarchical cache: The message mentions "~25 GB/s PCIe" for loading from CPU RAM, but doesn't account for the fact that 8 GPUs share the same PCIe fabric. With 8 GPUs simultaneously accessing CPU memory, the effective bandwidth per GPU is much lower, and contention could become a bottleneck.

Assumption that the 7.43 GB headroom is truly "wasted": The assistant characterizes the headroom as "left over" and "wasted," but CUDA graphs and activation memory are workload-dependent. Under heavy load with large batch sizes, activation memory can spike significantly. The 7.43 GB may be necessary headroom for peak workloads.

The Strategic Fork

The message ends with a question, not a recommendation. This is a deliberate choice. The assistant has done the investigative work and presented the options, but recognizes that the optimal path depends on the user's specific workload characteristics. This is a mature engineering judgment: the assistant provides the map and the compass, but lets the user choose the destination.

The user's response in the following message ([msg 5737]) — "What about hicache?" — indicates they were particularly interested in Option 2 (hierarchical cache), perhaps because it leverages the abundant system RAM (396 GB free) without the risk of OOM or the quality concerns of FP8. This would lead to the next phase of the conversation where hierarchical cache is enabled.

Conclusion

Message [msg 5736] is a masterclass in systems-level debugging and optimization consulting. It demonstrates how a seemingly simple question about RAM offloading led to a deep forensic analysis of GPU memory allocation, source code reverse-engineering, and the synthesis of multiple optimization strategies. The assistant's approach — gather data, trace the code, validate the formula, enumerate options, and ask a diagnostic question — is a model for how to approach complex performance optimization problems.

The message also reveals the hidden complexity of modern ML inference systems. The KV cache allocation formula rest_memory = available_gpu_memory - total_gpu_memory * (1 - mem_fraction_static) is not documented in SGLang's user-facing materials. It was discovered only through direct source code analysis. This kind of systems knowledge — the gap between documented behavior and actual implementation — is where the most impactful optimizations are often found.

In the end, the message transformed a vague feeling of constraint ("kinda low on max parallel request context") into a concrete, quantified understanding of GPU memory utilization, complete with a menu of actionable options. That is the essence of effective technical communication: turning confusion into clarity, and constraints into choices.