Satyam Agnihotri retweeted
started reading this beast of a book “LLM serving and optimizations” hosting LLMs at a scale. I will send the summary of Chapter-1 soon!
Satyam Agnihotri retweeted
Another great resource, the LLM Inference Handbook, from @Modular
handbook.modular.com/
"How to Scale Your Model," a book by Google DeepMind.
jax-ml.github.io/scaling-boo…
Using models for inference under constrained systems, such as automotive, is always an ongoing challenge. The model capabilities strain these systems, and in all such scenarios, understanding the scaling is of great importance, and this book delivers exactly that.
Satyam Agnihotri retweeted
Day 3: Inference Engineering
Reading all day
Read an analysis of vLLM's internals, breaking it down into the API, Engine Core, and Scheduler. Really informative and I enjoyed the visuals
Also read much more on speculative decoding. I read the original Medusa and EAGLE papers to see how the future predicted tokens are actually being generated.
TLDR is that Medusa adds several extra heads to the end of the transformer that receives the last layers residual stream and predicts the next tokens (so head 1 predicts token t+1, head 2 predicts token t+2, etc.). Very naive and clear why this doesn't really work in practice. It is trying to predict the t+3rd token without seeing the t+2nd.
EAGLE trains a much smaller auxiliary model (the draft model) and uses its quicker forward passes to get t+n tokens that the target model can then process in parallel. Basically (this is admittedly a simplification) imagine we feed Llama 70B a prompt. EAGLE proposes we give the exact same prompt to a much smaller, quantized, distilled version of Llama 70B and run the autoregressive decode loop for n (n=5,6,7ish) steps to get the next 5 potential tokens. Ok its actually not really the final tokens, its the 2nd to last layer residuals, which are then passed to the target model head, then passed back through the draft model to continue speculating and the target model to validate.
Then the target model (the 70B model) processes all of these tokens *in parallel* and accepts or denies the sequence. Usually the target model will only accept 2-4 tokens in a row from the draft model before it disagrees with what the next token should be, but this still speeds up inference by 2-3x.
Then once the target model tells the draft model it rejected its output at token t, it passes that token back to the draft model and it resumes its quick decode from token t to produce t+n more tokens to propose to the target model to then be accepted or rejected again.
I think there is so much alpha still to be done in speculative decoding it is hard to emphasize the extent to which we are in the earliest of innings with this technology. Information theory is going to be so important.
arxiv.org/pdf/2401.15077
vllm.ai/blog/2025-09-05-anat…
Speedrunning Inference Engineering
Day 2: First-order inference performance math
The goal of today is to create a small Python program that takes in model config, batch size, context length, hardware, etc. and outputs the estimated vLLM performance metrics in prefill and decode. This is more about deriving metrics from first principles to gain intuition than actually profiling a real system.
We want to predict: Model-weight memory, KV cache memory, decode FLOPs, decode memory traffic, compute and bandwidth lower bounds, decode iteration time and output throughput (TOPS), and whether a workload is weight, KV, or compute bound. Intrinsically, a load is always going to be either compute OR memory bound. Our goal in inference engineering is to bring this ratio as close to unity as possible such that compute and memory are waiting on one another as little as possible
To make these calculations, we need to get model params and GPU params. I am using the common mans Llama 3.1 8B Instruct and a single H100 (lets not overcomplicate things on day2)
Llama 3.1 8B model params are
hidden_size 4096 (d_model, thus each d_head = 4096 / 32 = 128)
intermediate_size 14336
num_hidden_layers 32
num_attention_heads 32
num_key_value_heads 8 (KV heads ≠ Q heads bc GQA model)
vocab_size 128256
torch_dtype bfloat16 (precision, no quantization)
max_position_embeddings 131072 (context length)
H100 params are
80GBs HBM3
700W peak power
132 SMs
3.35 TB/s HBM bandwidth
989 TFLOPs of BF16 compute
Obviuosly these are all peak performance numbers and represent an ideal. Most production systems rarely performance at these numbers for any significant duration of time, but our goal is to maximize that time.
Now for Llamas exact size to get an idea of where its parameter allocation lies
Token embeddings
525,336,576 params
1,050,673,152 bytes
Attention weights
1,342,177,280 params
2,684,354,560 bytes
MLP weights
5,637,144,576 params
11,274,289,152 bytes
Normalization
266,240 params
532,480 bytes
Output lm_head
525,336,576 params
1,050,673,152 bytes
Total
8,030,261,248 params
16,060,522,496 bytes
So we can see tat the vast majority of its weights still lay in the MLP, rather than attention or embedding/un-embedding (in the lm_head)
Now to calculate the total KV memory per user. If you remember from the last post, the equation is 2 * num_layers * num_kv_heads * d_head * precision * seq_length, so with the Llama numbers above at BF16 (2 bytes), a single token of KV adds 131KBs to the cache (~4KBs per layer)
So across seq_lenghts, we have
128 tokens → 16.0 MiB
1,024 tokens → 128.0 MiB
2,048 tokens → 256.0 MiB
4,096 tokens → 512.0 MiB
8,192 tokens → 1.0 GiB
16,384 tokens → 2.0 GiB
32,768 tokens → 4.0 GiB
65,536 tokens → 8.0 GiB
131,072 tokens → 16.0 GiB ← model's max context
Thus at max length, we have at most 32GiBs sitting in HBM. This cleanly fits in an H100, but the better question is how does the growing KV affect bandwidth and SM utilization?
Now to derive transformer linear layers FLOPs (the linear layers are just the matmuls that require linear projections, so q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, and down_proj)
For the attention projections, to get q_proj, we multiply a (1,4096) tensor by the (4096, 4096) W_Q matrix. Same math for our eventual o_proj.
So these each give us d^2 params for each of these matrices per token and thus 2d^2 FLOPs (forward pass FLOPs is just one addition and multiply per param). For the K and V projections, we only multiply a (1, 4096) tensor by the K and V (4096, 1024) matrices. These matrices have smaller ouptuts (1/4th the size of Q) because for every 4 Q heads, we only have 1 K and V head (again, the entire idea behind GQA). Therefore, each K and V projection gives us d * d_head * H_KV params. So total FLOPs for the attention projections is (4 * d^2) + (4 * d * (d_head * H_KV))
Now for the MLP layers, each SwiGLU MLP has three matrices (gate_proj, up_proj, and down_proj). The gate and up projection are in parallel, each taking the embedding (1, 4096) and computing (4096, 14336) taking the embedding now to be a (1, 14336) tensor.
When it is time for the activation, the up_proj computes a candidate value for each of the 14336 neurons, while the gate_proj acts as a learned valve on how strongly each neuron fires. So after each of these tensor operations, we run the non-linearity as h = silu(g) * u (we actually run the non-linearity on the gate projection, not on the up projection). Then, we take this product h and down_project it back into d_model space: (1, 14336) * (14336, 4096) -> (1, 4096). Thus we can see for all of these operation weoperate on 3 * d_model * d_ff (intermediate dimension) params and thus 6 * d_model * d_ff FLOPs
So in total we have (4 * d^2) + (4 * d * (d_head * H_KV)) + (6 * d_model * d_ff) FLOPs. Then we multiply this all by L layers. The last thing to remember is the final output projection (lm_head calculation) which takes the (1, 4096) embedding tensor and maps it to a vocab_size-d dimension vector (each dimension here representing a token probability).
Thus (1, 4096) * (4096, 128256) -> (1, 128256), which is the final vector yielding a probability for every possible next token. We take softmax on this, sample, and then return that next token. This shows we operate on d_model * vocab_size params and thus 2 * d_model * vobab_size FLOPs
Aggregating these terms across 32 Layers yields ≈ 15 GFLOPs per forward pass which is what we expect as the rough rule of thumb is 2 * num_params (this is a slighly overestimation as it ignores embedding params which is a row gather, not a matmul)
Now to calculate the decode attention FLOPs. For each generated token at context length N, each qeury head computes QK^T across all N (assuming dense attention) and a weighted sum of this dot product over all N value vectors. So o = qk^T and o * v
This operates on 2 * num_layers * seq_lenght * Q_h * d_head params, thus 4 * num_layers * seq_length * Q_h * d_head FLOPs. Since Q_h * d_head = d (bc Q_h = d / d_head), we can simplify to 4 * num_layers * seq_length * d. Thus, total decode FLOPs per sequence are F_fixed + (4 * num_layers * seq_length * d). We just derived F_fixed ≈ 15 GFLOPs, so total FLOPS ≈ 15 GLOPs + 4LNd
Across context lengths, this gives
Context: 128
Fixed FLOPs: 15.01 GF
Attention FLOPs: 0.07 GF
Total: 15.08 GF
Context: 2K
Fixed FLOPs: 15.01 GF
Attention FLOPs: 1.07 GF
Total: 16.08 GF
Context: 8K
Fixed FLOPs: 15.01 GF
Attention FLOPs: 4.29 GF
Total: 19.30 GF
Context: 16K
Fixed FLOPs: 15.01 GF
Attention FLOPs: 8.59 GF
Total: 23.60 GF
Context: 32K
Fixed FLOPs: 15.01 GF
Attention FLOPs: 17.18 GF
Total: 32.19 GF
Context: 64K
Fixed FLOPs: 15.01 GF
Attention FLOPs: 34.36 GF
Total: 49.37 GF
Context: 128K
Fixed FLOPs: 15.01 GF
Attention FLOPs: 68.72 GF
Total: 83.73 GF
Although, everyone knows that bc of the KV cache, decode is memory bound. We aren't really so concerned about FLOPs when most of the SMs are sitting idle waiting for memory. So lets model bandwidth. Per step, from HBM, the GPU needs to pull active weights and all KV cache and push one new KV token per sequence. So with context length = N and batches = B, to a first order approximation, Total Bandwidth = model_weights + (B * N * KV_user) + (B * KV_new_token).
Model weights are shared across the batch, in one decode step all sequences pass through the same layer together. KV traffic however is individual to each sequence (this is where continious batching will come in later). So roughly we can model memory byte movement growing as
B=1
128: 16.08
2K: 16.33
8K: 17.13
16K: 18.21
32K: 20.36
64K: 24.65
128K: 33.24
B=8
128: 16.20
2K: 18.21
8K: 24.65
16K: 33.24
32K: 50.42
64K: 84.78
128K: OOM
B=16
128: 16.33
2K: 20.36
8K: 33.24
16K: 50.42
32K: 84.78
64K: OOM
128K: OOM
B=32
128: 16.60
2K: 24.65
8K: 50.42
16K: 84.78
32K: OOM
64K: OOM
128K: OOM
B=64
128: 17.14
2K: 33.25
8K: 84.79
16K: OOM
32K: OOM
64K: OOM
128K: OOM
B=128
128: 18.22
2K: 50.44
8K: OOM
16K: OOM
32K: OOM
64K: OOM
128K: OOM
OOM = crossing 64.7 GiB which saturates H100 total HBM when including weights
Ok now with all of this info lets try calculating memory and compute bounds.
If we were memory bound (i.e. have ample idle compute), the time per decode step would be memory_per_step / HBM_BW. We just calculated memory_per_step is dependent on context and batching. So to see this in action I had Claude make a chart (first one attached)
We calculated that F_step = B * F_decode_per_token = B * (F_fixed+ 4LNd). So if we were compute bound, time per decode step would be F_step / H100_FLOPs (H100 FLOPs = 989TFLOPs). This is represented in the second chart attached. However because there is so much more compute on an H100 than relative memory, we are essentially never compute bound (except at high batch prefill). This shows up clearly comparing the two charts.
I think going forward I am going to make these way shorter and more approachable. Or do people like them long and dense like this? Lmk!
Satyam Agnihotri retweeted
This playlist is super underrated when it comes to understanding the engineering behind trillions parameter LLM inference from first principles by @thecommitlog
Save it for your weekend watch :
youtube.com/playlist?list=PL…
Satyam Agnihotri retweeted
POV: you’re trying to be a man of God but the world keeps trying to break you apart
Satyam Agnihotri retweeted
While scrolling through my gallery, I found this image.
This was my very first income back in 2024.
₹2,500.
It's funny looking back now, but every journey starts somewhere. Grateful for that first step—it meant a lot more than the amount itself. ❤️
Satyam Agnihotri retweeted
yoo chat
if you are looking for any repositories to dive deep on the topic of docker image optimization than have a look at this one .... hope it helps you
github.com/ag-chirag/ai-dock…
Someone just shipped a full Kubernetes cluster that runs inside your browser. 🤯
Almost nobody has found this yet.
This is Webernetes. ngrok built it because they wanted to create interactive Kubernetes content without spinning up real cloud infrastructure for every user.
Here is what actually works inside it:
→ Full 3 node cluster boots up entirely in your browser tab
→ Pods, Deployments, ReplicaSets and Services all behave like real Kubernetes
→ Pods get actual IP addresses and talk to each other over HTTP
→ RollingUpdate and Recreate deployment strategies both fully supported
→ Real Kubernetes events fire exactly the same way they do in a live cluster
→ Embed a full working cluster inside any web app with one npm install
Every Kubernetes tutorial makes you provision a cluster before you learn anything. Webernetes removes that friction entirely.
This is one of the most creative things I have seen in the Kubernetes space this year.
Repo: github.com/ngrok/webernetes
Satyam Agnihotri retweeted
If AI can write your Kubernetes manifest, your value is not YAML.
Things to stop flexing about:
- "I can write Dockerfiles from scratch"
- "I memorised all of kubectl"
- "I know CircleCI/Jenkins/GitLab CI really well"
Things to quietly get good at:
- Reading tcpdump when a gRPC call randomly hangs
- Using strace/ltrace to see what your service is actually waiting on
- Following a request through Envoy, Kafka, Postgres when latency jumps
- Knowing when "eventual consistency" will corrupt user flows
The leverage is shifting from writing config to understanding failure modes in production.
Cluster Agent Swarm Skills is a collection of specialized AI agent skills for Kubernetes and OpenShift operations, covering cluster management, GitOps, security, observability, incident response, and platform workflow orchestration
➤ ku.bz/n9K3N9JBq
Satyam Agnihotri retweeted
Kubernetes New Contributor Orientation for 2026-06-16 youtube.com/watch?v=Pz1fXbja…
Satyam Agnihotri retweeted
Shipped my first GitHub release, a C++ CLI tool,
took almost 5 hours,
Now you can clean your whole laptop from all the garbage like node_modules, venv, or builds .
presenting "TRASHCTL", a tool that scans and cleans your pc in seconds.
check it out :
github.com/Piyushrathoree/Tr…
Satyam Agnihotri retweeted
I just generated FOUR pixel-perfect, fully responsive landing pages by typing absolute garbage prompts into Gemini 3.0.
1–2 prompts each.
Zero code changes.
All deployed and live in under 15 minutes total.
This is actually broken.
Satyam Agnihotri retweeted
Learn Linux, networking, containers, and Kubernetes by solving hands-on problems 🛠️
A curated collection of over 100 carefully crafted challenges - with interactive checks, clear diagrams, and helpful theoretical references.
Like LeetCode but for DevOps labs.iximiuz.com/challenges
Satyam Agnihotri retweeted
IO devices and latency by @BenjDicken
A cool animated explainer about i/o devices and associated latency
planetscale.com/blog/io-devi…
How CPUs Interact with So Many Different Devices by Core Dumped
youtu.be/tadUeiNe5-g
Satyam Agnihotri retweeted
> CS @ Stanford
> Founded Loopt (sold for $43.4M)
> Founded Hydrazine Capital (VC fund)
> President, Y Combinator (2014-2019)
> Raised $1B+ for OpenAI (Microsoft)
> Founded Worldcoin (crypto startup)
> Forbes 30 Under 30 (2015)
> TIME 100 Most Influential (2023)








































