Starphix
Systems Engineering / Sources
The Lab / Sources / Systems Engineering / Inference/Batching-And-Quantization.md

Batching and Quantization

Continuous batching, PagedAttention, and quantization — the three levers that determine inference throughput, latency, and cost per token.

Continuous Batching

Traditional batching waits for all requests in a batch to complete before starting the next batch. Continuous batching (iterative batching) adds new requests to a running batch as others finish, keeping GPUs saturated.

graph TD
    subgraph Traditional Batching
        A[Batch 1: Req 1,2,3] --> B[Wait for all to finish]
        B --> C[Batch 2: Req 4,5,6]
        C --> D[Idle GPU during tail latency]
    end

    subgraph Continuous Batching
        E[Running Batch: Req 1,2,3] --> F[Req 1 finishes]
        F --> G[Add Req 4 to batch]
        G --> H[Req 2 finishes]
        H --> I[Add Req 5 to batch]
        I --> J[GPU stays saturated]
    end

    style D fill:#ffcdd2
    style J fill:#e8f5e9
Metric Traditional Batching Continuous Batching
GPU utilization Variable (idle during tail) Consistently high
Throughput Lower Higher
Latency (p99) Higher (head-of-line blocking) Lower
Implementation complexity Simple Complex (PagedAttention required)

PagedAttention

PagedAttention (vLLM) manages the KV cache like virtual memory: non-contiguous blocks, reducing fragmentation and waste.

# Conceptual PagedAttention block table
block_table = {
    "request_1": [block_10, block_25, block_41],   # non-contiguous
    "request_2": [block_11, block_26],              # shares block 10's content if prefix match
    "request_3": [block_12, block_27, block_42],
}

# With prefix caching, shared prefixes map to same blocks
# "What is the capital of" -> blocks [10, 25]
# Req 1: "What is the capital of France?" -> [10, 25, 41]
# Req 2: "What is the capital of Germany?" -> [10, 25, 26]
Benefit Impact
Reduced KV-cache waste 2–4x more sequences per GPU
Larger max batch size Higher throughput at same latency
Prefix caching Shared prompts computed once

Quantization Deep Dive

Post-Training Quantization (PTQ)

Quantize a pre-trained model without retraining.

Method Calibration Quality Speed Use Case
INT8 (static) Small dataset Good Fast General edge deployment
INT8 (dynamic) None Good Fast Activations vary widely
INT4 (AWQ) Calibration set Very good Medium LLMs, production edge
INT4 (GPTQ) Calibration set Very good Medium LLMs, one-shot
INT4 (GGUF) None Good Fast llama.cpp, local inference

Quantization-Aware Training (QAT)

Simulate quantization during training so the model adapts to lower precision.

Method When to Use Cost Quality
QLoRA Fine-tuning on limited GPU Low Near full-precision
LoftQ Combined quantization + LoRA Medium Good
QAT (full) Maximum quality at low precision High Best

Decision Guidance

Situation Recommended Approach
Maximize throughput on GPU cluster Continuous batching + PagedAttention (vLLM/SGLang)
Shared prefixes across requests (RAG, chat) Enable prefix caching (RadixAttention/PagedAttention)
Deploy on edge / mobile INT4 quantization (GGUF/AWQ) + small model
Quality-critical, some headroom INT8 quantization
Fine-tuning on limited hardware QLORA (4-bit)

Anti-Patterns

Anti-Pattern Fix
No batching under load Enable continuous batching; measure throughput at target QPS
Ignoring KV-cache memory Set gpu_memory_utilization and max_model_len; monitor OOM
Quantizing without calibration Use representative dataset for AWQ/GPTQ; validate quality after
Full precision on memory-constrained device INT4 quantization + memory-mapped loading
No prefix caching for repetitive prompts Enable prefix caching; measure cache hit rate
  • Inference Engines — vLLM, SGLang, TensorRT-LLM engine comparison
  • Quantization For Edge — Edge-specific quantization techniques
  • Inference Optimization — vLLM, SGLang, speculative decoding
  • Observability — Monitoring throughput, cost per token, KV-cache usage

vLLM Batching Configuration

from vllm import LLM

llm = LLM(
    model="meta-llama/Llama-3.1-70B-Instruct",
    tensor_parallel_size=8,
    gpu_memory_utilization=0.95,   # Higher = larger batches, more OOM risk
    max_model_len=8192,
    enable_prefix_caching=True,    # Critical for RAG / multi-turn
    swap_space=16,                 # CPU swap space for KV cache overflow (GiB)
    max_num_seqs=256,              # Max sequences in continuous batch
    max_num_batched_tokens=4096,   # Max tokens per batch iteration
)

Quantization Quality Validation

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

def evaluate_quantization(original_model_path, quantized_model_path, test_prompts):
    tokenizer = AutoTokenizer.from_pretrained(original_model_path)

    original = AutoModelForCausalLM.from_pretrained(original_model_path)
    quantized = AutoModelForCausalLM.from_pretrained(quantized_model_path)

    results = []
    for prompt in test_prompts:
        inputs = tokenizer(prompt, return_tensors="pt")

        with torch.no_grad():
            orig_out = original.generate(**inputs, max_new_tokens=50)
            quant_out = quantized.generate(**inputs, max_new_tokens=50)

        orig_text = tokenizer.decode(orig_out[0], skip_special_tokens=True)
        quant_text = tokenizer.decode(quant_out[0], skip_special_tokens=True)

        results.append({
            "prompt": prompt,
            "original": orig_text,
            "quantized": quant_text,
            "match": orig_text == quant_text,
        })

    return results

Sources

  • vLLM PagedAttention paper (Kwon et al.), SGLang RadixAttention paper (Wang et al.), AWQ (Lin et al.), GPTQ (Frantar et al.), QLoRA (Dettmers et al.), GGUF/llama.cpp, NVIDIA TensorRT-LLM.*

From the Systems Engineering research vault · CC BY 4.0 · all notes · raw markdown