GenAIHub
← Back to Technical Section

Optimizing Speed & Efficiency for Local LLMs

Quantization, Flash Attention, KV Cache & Practical Techniques to Run Large Models on Limited Hardware

1. Understanding the Problem

Modern Large Language Models can have tens or hundreds of billions of parameters. In standard precision (float32), a model with 100 B parameters would require over 200 GB of memory just to load the weights — impossible on consumer hardware. Even smaller models (7 B–70 B) can be impractical without careful optimization.

Key Insight: The three main bottlenecks for local LLM inference are parameter loading (memory), attention computation (CPU/GPU), and KV cache growth (memory over long contexts). Optimizing all three is essential for acceptable performance without expensive hardware.

The Three Bottlenecks

Parameter Loading

Billions of weights must fit in RAM/VRAM. A 70 B model in FP16 needs ~140 GB — far exceeding most GPUs.

Attention Computation

Self-attention scales quadratically with sequence length. For long contexts (32 k+ tokens), this becomes the dominant compute cost.

KV Cache Growth

During autoregressive generation, keys and values for all prior tokens accumulate — consuming memory that can surpass the model weights themselves.

Quick Memory Estimation

# Approximate memory for model weights
memory_gb = (num_parameters * bytes_per_param) / (1024 ** 3)

# Examples:
#   7B  @ FP16 (2 bytes)  =  ~14 GB
#   7B  @ Q4   (0.5 bytes) = ~3.5 GB
#  70B  @ FP16 (2 bytes)  = ~140 GB
#  70B  @ Q4   (0.5 bytes) = ~35 GB

# KV Cache per token (approximate, per layer):
kv_per_token = 2 * num_layers * hidden_dim * bytes_per_element
# For a 32-layer model with hidden_dim=4096 @ FP16:
#   kv_per_token = 2 * 32 * 4096 * 2 = 512 KB per token
#   At 32k context: ~16 GB just for KV cache!

2. Quantization — Reducing Precision for Speed & Memory Gains

Quantization is the technique of reducing the numerical precision of model weights and activations — for example, from 32 bits to 16, 8, or even 4 bits. This is the single most impactful optimization for running LLMs locally.

Why Quantize? Quantized models consume less memory, enable faster inference, allow large models to run on smaller GPUs or CPUs, and dramatically reduce RAM/VRAM requirements with minimal quality loss.

Types of Quantization

Post-Training Quantization (PTQ)

Applied to already-trained models without recalibration. The simplest and most widely used approach (e.g., GPTQ, AWQ, GGUF formats).

Dynamic Quantization

Values are quantized at inference time. Activations stay in higher precision during training; only the weights are pre-quantized for storage/loading.

Static Quantization

Weights and activations remain permanently in low bit-width. Requires a calibration dataset. Fastest at inference but needs careful tuning.

Quantization Levels Comparison

Format Bits Memory Saving Quality Impact Common Use
FP32 32 Baseline None (full precision) Training only
FP16 / BF16 16 ~50% Negligible Balance between quality & memory
Q8 / INT8 8 ~75% Very small Good for most general tasks
Q4_K_M / INT4 4 ~87% Small — excellent trade-off Recommended baseline for local use
Q2 / 2-bit 2 ~93% Noticeable — test carefully Maximum memory savings

Popular Quantization Formats & Tools

GGUF (llama.cpp)

The standard format for CPU and mixed CPU/GPU inference. Supports Q2–Q8 with K-quant variants (Q4_K_M, Q5_K_S, etc.). Used by Ollama, LM Studio, and llama.cpp.

GPTQ

GPU-focused quantization using calibration data for better accuracy. Widely used with Hugging Face Transformers and vLLM. Best for pure GPU inference scenarios.

AWQ (Activation-Aware Quantization)

Preserves important weight channels based on activation analysis. Often better quality than GPTQ at the same bit-width. Supported by vLLM and TGI.

BitsAndBytes (BnB)

Integrated into Hugging Face Transformers. Supports 8-bit and 4-bit quantization (NF4, FP4) on-the-fly. Easiest to use with the Transformers library.

Trade-off: The lower the bit-width, the greater the memory savings and speed potential — but there may be incremental degradation in output quality. Q4_K_M is generally considered the sweet spot for most local use cases.

3. Flash Attention — Optimized Attention Algorithms

Flash Attention is a more efficient method for computing attention in Transformers. It reduces memory movement and improves speed by reorganizing computations to fit better in GPU caches (SRAM), avoiding the need to materialize the full N×N attention matrix in HBM.

Standard Attention Q, K, V Full N×N in HBM Output O(N²) memory Slow HBM reads/writes Flash Attention Q, K, V Tiled blocks in SRAM Output O(N) memory Fast SRAM tiling

Key Benefits

Reduced Memory Usage

Instead of materializing the full N×N attention matrix, Flash Attention computes it in tiles, reducing memory from O(N²) to O(N).

Longer Sequences

By dramatically cutting memory overhead, Flash Attention enables processing much longer sequences (32 k, 64 k, 128 k+ tokens) on the same hardware.

Faster Inference

By keeping data in fast SRAM (on-chip cache) and minimizing slow HBM (off-chip memory) accesses, computation is 2–4x faster.

No Quality Loss

Flash Attention computes the exact same result as standard attention — it’s a computational optimization, not an approximation.

Flash Attention Versions

Version Key Improvements Hardware
Flash Attention 1 Tiled attention, O(N) memory, IO-aware Ampere (A100) and newer
Flash Attention 2 Better parallelism, reduced non-matmul FLOPs, ~2x faster than v1 Ampere, Ada Lovelace
Flash Attention 3 FP8 support, async computation, warp specialization, 1.5–2x faster than v2 Hopper (H100) and newer

Tip: Most modern LLM frameworks (llama.cpp, Ollama, vLLM, Hugging Face Transformers) already support Flash Attention. In many cases, enabling it is as simple as passing a configuration flag or setting an environment variable.

4. KV Cache — Key-Value Cache Management

Transformers use self-attention where each new token depends on computations from all previous tokens. The KV Cache stores the Keys (K) and Values (V) for tokens that have already been processed, avoiding costly recomputation during generation.

Why it matters: Without KV Cache, text generation would be extremely slow — each new token would need to recompute attention over the entire sequence from scratch. But the cache grows linearly with context length and can consume more memory than the model weights themselves in long conversations.

Autoregressive Generation with KV Cache Token 1 Token 2 Token 3 ... Token N KV Cache (stored K & V for tokens 1..N) New Token Attention (only new token) Output Reused (no recomputation) Only this is computed

KV Cache Optimization Techniques

Quantized KV Cache

Store K/V tensors at lower precision (e.g., FP16 → FP8 or INT4). Can reduce cache memory by 50–75% with minimal quality impact. Combining Q8 KV cache with Flash Attention can cut memory usage by ~66%.

Paged Attention (vLLM)

Manages KV cache in “pages” allocated on demand (like OS virtual memory). Eliminates wasted memory from over-allocation. Can improve throughput by 2–4x compared to naive allocation.

GQA / MQA

Grouped Query Attention (GQA) and Multi-Query Attention (MQA) use fewer KV heads than query heads, reducing the KV cache size per token. Most modern models (Llama 3, Mistral, etc.) already use GQA.

Prefix / Prompt Caching

Store KV cache for common prompt prefixes (system prompts, instructions) and reuse across requests. Significantly reduces time-to-first-token for repeated interactions with similar prompts.

5. Practical Optimization Pipeline — Step by Step

Follow this recommended pipeline to optimize any local LLM for speed and efficiency. Each step builds on the previous one for cumulative gains.

Step 1 — Start with a Quantized Model

Download or select a pre-quantized model. Q4_K_M is the recommended baseline — it offers an excellent balance between memory, speed, and quality.

# Ollama - download a Q4 quantized model
ollama pull llama3.1:8b-instruct-q4_K_M

# llama.cpp - use a GGUF model directly
./llama-server -m models/llama-3.1-8b-instruct-Q4_K_M.gguf

# Hugging Face + BitsAndBytes - load in 4-bit
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-8B-Instruct",
    load_in_4bit=True,
    device_map="auto"
)

Step 2 — Enable Flash Attention

Activate Flash Attention wherever possible. This speeds up inference without any quality penalty.

# llama.cpp / Ollama - enable Flash Attention flag
./llama-server -m model.gguf --flash-attn

# Hugging Face Transformers
model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-8B-Instruct",
    attn_implementation="flash_attention_2",  # or "sdpa"
    torch_dtype=torch.float16,
    device_map="auto"
)

# vLLM - Flash Attention is enabled by default
# Just ensure your GPU supports it (Ampere+)

Step 3 — Quantize the KV Cache

This drastically reduces memory consumption for long contexts. Combining Q8 KV cache with Flash Attention can reduce memory by ~66%.

# llama.cpp - quantized KV cache
./llama-server -m model.gguf \
    --flash-attn \
    --cache-type-k q8_0 \
    --cache-type-v q8_0

# For more aggressive compression:
./llama-server -m model.gguf \
    --flash-attn \
    --cache-type-k q4_0 \
    --cache-type-v q4_0

Step 4 — Adjust Precision and Test

Not all models respond equally to each configuration. Empirical testing is essential.

  • If generation has artifacts or quality loss → increase precision (Q8 or FP16)
  • If memory is the bottleneck → try Q2 or Q4 for maximum savings
  • Monitor tokens per second and VRAM usage as key metrics
  • Compare outputs between different quantization levels for your specific task

Step 5 — Adjust Context Size

If you need longer contexts (32 K, 64 K, 128 K), follow this order:

  1. Enable Flash Attention (required for long contexts)
  2. Enable quantized KV Cache
  3. Set the context length parameter
  4. Test and monitor memory usage
# llama.cpp - set context to 32K with optimized cache
./llama-server -m model.gguf \
    --ctx-size 32768 \
    --flash-attn \
    --cache-type-k q8_0 \
    --cache-type-v q8_0

# Ollama - set context in Modelfile
# FROM llama3.1:8b-instruct-q4_K_M
# PARAMETER num_ctx 32768

Warning: Larger context = more memory usage, even with quantization and Flash Attention. A 128 K context can require 10–20+ GB of additional memory just for the KV cache.

6. Advanced Optimization Techniques

Beyond the core techniques, several advanced approaches can provide additional gains.

Prompt / Context Caching

Store pre-computed KV cache for repeated prompt prefixes (e.g., system instructions). Significantly reduces time-to-first-token for repeated interactions. Supported by llama.cpp (prompt caching) and vLLM (automatic prefix caching).

Speculative Decoding

Use a smaller “draft” model to generate candidate tokens in parallel, then verify with the main model. Can speed up generation by 2–3x when the draft model is accurate. Supported in llama.cpp and vLLM.

Continuous / Dynamic Batching

Group multiple requests together and process them simultaneously to maximize GPU utilization. vLLM and TGI handle this automatically. Essential for multi-user / production scenarios.

Mixed CPU/GPU Offloading

Split model layers between GPU and CPU RAM. Keep the most compute-intensive layers on GPU, offload the rest to CPU. Allows running larger models on smaller GPUs at the cost of some speed.

Efficient Attention Architectures

Architectural variants like Multi-Query Attention (MQA), Grouped-Query Attention (GQA), and Sliding Window Attention reduce memory and compute requirements at the model design level. Most modern models already incorporate these.

TensorRT-LLM / Triton

NVIDIA’s TensorRT-LLM combines kernel fusion, quantization (FP8/INT4), Flash Attention, and paged attention into an optimized inference engine. Offers the best performance on NVIDIA GPUs but requires more setup.

Cutting-Edge Research: Methods like KVLinC, KIVI, and VecInfer show that aggressive KV cache quantization (down to 2-bit) with correction techniques can maintain quality while achieving 2–3x speed improvements in long-context generation tasks.

7. Local LLM Framework Comparison

Several frameworks are popular for running LLMs locally. Each has different strengths.

Framework Best For Quantization Flash Attn KV Cache Opt Hardware
llama.cpp CPU + GPU, wide hardware support GGUF (Q2–Q8) Yes Quantized KV CPU, NVIDIA, AMD, Apple Silicon
Ollama Easy setup, OpenAI-compatible API GGUF (via llama.cpp) Yes Quantized KV CPU, NVIDIA, AMD, Apple Silicon
vLLM High throughput, production serving AWQ, GPTQ, FP8 Yes (default) Paged Attention NVIDIA GPU (primary)
LM Studio Desktop GUI, beginner-friendly GGUF (via llama.cpp) Yes Quantized KV CPU, NVIDIA, Apple Silicon
HF Transformers Research, flexibility, fine-tuning BnB, GPTQ, AWQ Yes (SDPA/FA2) Via libraries NVIDIA GPU (primary)
TensorRT-LLM Maximum NVIDIA GPU performance FP8, INT4, INT8 Yes (fused kernels) Paged + Quantized NVIDIA GPU only

8. Key Metrics & Monitoring

When optimizing inference, measure these indicators to track improvements:

Metric What It Measures Target
Time to First Token (TTFT) Time from request to first generated token < 1s for interactive use
Tokens per Second (TPS) Generation speed during decoding > 20 tok/s for readable streaming
Memory Footprint Total RAM/VRAM usage (weights + KV cache + overhead) Within available hardware limits
Throughput Total tokens processed per second across all requests Maximize for multi-user scenarios
Quality (Perplexity) Output quality compared to full-precision baseline Minimal degradation vs. FP16
# Benchmark with llama.cpp (built-in benchmarking)
./llama-bench -m model.gguf -p 512 -n 128 -ngl 99

# Ollama - check performance in verbose mode
OLLAMA_DEBUG=1 ollama run llama3.1:8b

# vLLM - built-in benchmarking
python -m vllm.entrypoints.openai.api_server \
    --model meta-llama/Llama-3.1-8B-Instruct \
    --dtype auto --max-model-len 4096

9. Best Practices Summary

Golden Rule: Always combine techniques — quantization + Flash Attention + optimized KV cache is more effective than any single technique alone.

Technique Impact Overview

Technique Impact Quality Trade-off Ease of Implementation
Weight Quantization High Slight quality loss Easy
Flash Attention High No loss Easy
Quantized KV Cache Medium–High Minimal loss Easy
Prompt Caching Medium No loss Moderate
Speculative Decoding Medium No loss (mathematically exact) Moderate
Paged Attention High (multi-user) No loss Easy (use vLLM)
Efficient Architectures (GQA/MQA) Variable Built into model N/A (model choice)
  • Combine methods: Quantization + Flash Attention + KV Cache optimization is far more effective than each technique alone
  • Monitor metrics: Track latency per token, VRAM usage, and throughput
  • Adapt to hardware: Some quantizations and accelerations depend on CPU/GPU architecture
  • Test empirically: Results vary by model, task, and hardware — always benchmark
  • Start conservative: Begin with Q4_K_M + Flash Attention, then optimize further as needed
  • Consider your workload: Single-user vs. multi-user scenarios need different optimization strategies

10. Use Cases

πŸ’»

Local Code Assistant

Fast, private coding help

πŸ”’

Privacy-First Chat

No data leaves your machine

πŸ“„

Document Analysis

RAG with local models

🏒

On-Premise Deployment

Enterprise LLM serving

πŸ§ͺ

Research & Prototyping

Iterate without API costs

πŸ“±

Edge / Embedded AI

Run on constrained devices

11. Essential Resources

12. Related Topics

Test Your Knowledge

Score 8/10 or higher to pass