Running GLM 5.2 on a Potato: Quantization & Memory Tricks That Work
The Potato Experiment: What Happened
A developer known as JustVugg did something that makes ML engineers both wince and grin: they got GLM 5.2 running on a machine that, by all reasonable standards, shouldn't be able to touch a model of that class. The project, Colibri, is a dead-simple wrapper that combines aggressive quantization, CPU offloading, and careful memory management to squeeze a 12B-parameter model onto consumer hardware with limited VRAM.
GLM 5.2 (General Language Model) is a dense transformer model in the same weight class as LLaMA-2 13B or Mistral-Nemo. Normally, running inference on a 12B model at half precision (FP16) requires roughly 24 GB of VRAM just to load the weights. Add key-value (KV) cache overhead for context, and you're easily pushing 28-30 GB. Most consumer GPUs top out at 8-16 GB. The machine in question likely had an RTX 3060 (12 GB) or similar—a solid card, but nowhere near the headroom for a full-fat 12B model.
What JustVugg achieved was loading the model in 4-bit quantized form, using the CPU as a fallback memory pool for layers that couldn't fit on the GPU, and still getting token generation speeds that were usable (not fast, but coherent). The interface is a simple Gradio web UI, but the real engineering is in the backend: a carefully tuned llama.cpp-style pipeline that treats your entire system—RAM, swap, GPU VRAM—as a single heterogeneous memory pool.
Why This Matters for Engineers
This isn't just a neat hack. It's a preview of how most inference will work for the next few years. Three reasons:
1. The hardware gap is structural. Model sizes are growing faster than consumer VRAM. A 12B model was "big" in 2023; now we have 70B, 405B, and mixture-of-experts architectures that still need to load shared layers. Even with HBM3 on datacenter GPUs, the economics push toward smaller, quantized models running on edge devices or repurposed gaming rigs.
2. Quantization is no longer a compromise—it's a requirement. The Colibri project uses 4-bit quantization (likely Q4_K_M or similar from the llama.cpp ecosystem). At 4 bits per weight, a 12B model shrinks from 24 GB to approximately 6-7 GB for the weights alone. That's the difference between "doesn't run" and "runs with room for context." The quality loss from well-tuned 4-bit quantization is often negligible for chat, summarization, and RAG tasks.
3. Memory hierarchy awareness is becoming a core competency. Engineers who understand when to keep tensors on GPU, when to stream from CPU RAM, and when to hit NVMe aren't just optimizing—they're enabling. The Colibri approach of offloading specific layers to CPU while keeping attention heads on GPU is a pattern we'll see baked into frameworks over the next 18 months.
The Core Tricks: Quantization Deep Dive
Quantization reduces the numerical precision of model weights and activations. The key insight: neural networks are overparameterized. Most weights don't need 16 bits of precision; 4 bits often suffice if you handle outliers correctly.
What's happening under the hood:
- Weight quantization: Each weight matrix is chunked into blocks (typically 32 or 64 values). Per-block, the code computes a scaling factor and a zero-point, then maps the 16-bit floats to 4-bit integers. This is block-wise asymmetric quantization—the workhorse of
llama.cpp's Q4_K and Q5_K formats. - Mixed precision: Not all layers are equal. Attention output projections and the first/last layers are more sensitive. Formats like Q4_K_M use slightly higher precision (6-bit) for these critical tensors while keeping the bulk at 4-bit. This is the "M" in Q4_K_M—medium, balancing size and quality.
- Dequantization at runtime: During inference, weights are dequantized back to FP16 on the fly as they're multiplied. This adds a small compute overhead but saves massive memory bandwidth. On memory-bound hardware (which consumer GPUs are), the tradeoff is strongly net-positive.
Here's a simplified view of what the quantization step looks like in code (conceptual, not from Colibri directly):
# Conceptual block-wise quantization
import torch
def quantize_block(weight_block, bits=4):
# Find min/max for the block
w_min, w_max = weight_block.min(), weight_block.max()
# Compute scale and zero-point
scale = (w_max - w_min) / (2**bits - 1)
zero_point = torch.round(-w_min / scale)
# Quantize
quantized = torch.clamp(
torch.round(weight_block / scale) + zero_point, 0, 2**bits - 1
).to(torch.uint8)
return quantized, scale, zero_point
In practice, Colibri almost certainly relies on pre-quantized GGUF files, which bake these quantization parameters into a well-defined file format. The heavy lifting is done at conversion time; inference just reads and dequantizes.
Memory Offloading: CPU, Disk, and the Memory Hierarchy
Quantization alone might not be enough. Even at 4-bit, a 12B model with 4k context can push past 8 GB when you include KV cache, activations, and framework overhead. This is where offloading comes in.
The memory hierarchy, from fastest to slowest:
| Tier | Typical Size | Bandwidth | Use Case |
|---|---|---|---|
| GPU VRAM (GDDR6/HBM) | 8-24 GB | 500-1000 GB/s | Attention heads, active layers |
| CPU RAM (DDR4/DDR5) | 32-64 GB | 50-100 GB/s | Offloaded FFN layers, KV cache overflow |
| NVMe SSD | 1-4 TB | 3-7 GB/s | Emergency swap, model loading |
Colibri's approach (inferred from the repo structure and llama.cpp conventions) likely uses layer-wise offloading. The model is split into transformer layers. The most latency-sensitive layers—attention query/key/value projections—stay on GPU. Feed-forward network (FFN) layers, which are larger but less latency-critical, get pushed to CPU RAM. The framework streams FFN weights to GPU as needed, overlapping compute with transfer.
This is not trivial. Naive offloading tanks performance because the GPU sits idle waiting for weights. Effective offloading requires:
- Pipelining: While the GPU computes layer n, the CPU asynchronously preloads layer n+1's weights.
- Layer-aware splitting: Not all layers are created equal. The first embedding layer and final LM head are small but critical—keep them on GPU. Middle FFN blocks are huge and parallelizable—offload aggressively.
- KV cache management: The KV cache grows linearly with sequence length. Smart offloading moves older KV entries to CPU RAM, keeping only the most recent tokens in VRAM. This is sometimes called "rolling cache" or "sliding window attention with CPU backup."
How to Try It Today: A Practical Path
You don't need to clone Colibri specifically (though you can—it's on GitHub). The broader ecosystem has matured to the point where you can replicate this with off-the-shelf tools. Here's the fastest path:
Step 1: Get a quantized model. Head to Hugging Face and search for "GLM-4-9B-GGUF" or similar. Look for Q4_K_M variants from reputable quantizers like TheBloke or bartowski. Download the .gguf file—this is your pre-quantized, ready-to-run model.
Step 2: Pick a runtime. You have three solid options:
- llama.cpp: The reference implementation. Command-line, highly configurable, supports GPU offloading with
-nglflag (number of GPU layers). - Ollama: Wraps
llama.cppin a user-friendly daemon.ollama run glm4:9b-q4_K_Mhandles quantization and offloading automatically. - LM Studio: GUI-based, good for experimentation. Lets you visually drag a slider for GPU offload percentage.
Step 3: Tune your offloading. Start with all layers on GPU if they fit. If you get OOM errors, reduce the GPU layer count. In llama.cpp:
./main -m glm4-9b-q4_k_m.gguf -ngl 20 -c 4096
This loads 20 layers on GPU, rest on CPU. Adjust until you find the sweet spot where VRAM usage is around 90%. Watch nvidia-smi in another terminal.
Step 4: Measure, don't guess. Token generation speed (tokens/second) is your key metric. If you're below 2-3 tok/s, the experience is painful for chat. Target 5-10 tok/s for usable interactive chat. If you're hitting 15+ tok/s on a 12 GB card, you've won.
A Balanced Take: Performance vs. Practicality
Let's be blunt about tradeoffs.
What you gain:
- Access to models that were previously out of reach.
- Local, private inference with no API costs.
- A deep understanding of the memory hierarchy that will serve you well as models evolve.
What you lose:
- Speed. A 4-bit quantized 12B model with CPU offloading might run at 3-8 tok/s on a mid-range GPU. The same model on an A100 runs at 100+ tok/s. For batch processing or high-throughput applications, this is a non-starter.
- Quality headroom. While 4-bit quantization is surprisingly good, it's not lossless. Perplexity increases by 0.5-2 points depending on the benchmark. For code generation or precise factual recall, you might notice more hallucinations or syntax errors.
- Context length. KV cache memory scales with sequence length. A 32k context window on a 12 GB card is extremely tight even with 4-bit quantization. Most users cap at 4k-8k tokens.
The real insight: This approach is best for interactive chat, document Q&A, and prototyping. It's not for production serving. If you're building a customer-facing app, use a quantized model by all means—but run it on a GPU with enough VRAM to hold the entire model and cache, or use a hosted API. The Colibri-style setup is your local dev environment, not your deployment target.
What's next: We're seeing the emergence of "hybrid" inference where multiple machines or multiple memory tiers cooperate. Apple's MLX framework already does seamless unified memory on M-series chips. NVIDIA's Grace-Hopper architecture points toward CPU-GPU cache coherence. The techniques demonstrated in Colibri—manual, hacky, effective—are the early signals of where the industry is heading.
FAQ
Q: Can I run a 70B model on an 8 GB GPU with these tricks? A: Technically yes, but you'll get 0.1-0.5 tok/s. The model weights alone at 4-bit are ~35 GB. You'd be streaming from NVMe, and the GPU would spend most of its time waiting. Stick to models where the quantized weights are at most 1.5-2x your VRAM size.
Q: Does quantization affect fine-tuning? A: Yes. You can't fine-tune a quantized model directly—the gradients require higher precision. You'd fine-tune in FP16/BF16, then quantize the result. QLoRA is a technique that fine-tunes by adding small trainable adapters to a frozen quantized base model.
Q: Is GLM 5.2 specifically better for this than LLaMA or Mistral? A: Not inherently. The techniques apply to any transformer model. GLM's architecture (bidirectional attention with autoregressive blank infilling) doesn't change the memory equation. Pick the model that performs best on your task; the quantization and offloading work similarly across architectures.
Q: How do I know if my quantization is hurting quality? A: Run a small eval set. Take 50-100 prompts, generate completions at FP16 and at 4-bit, and compare. Look for factual errors, code that doesn't compile, or nonsensical outputs. Tools like lm-evaluation-harness automate this. A 1-3% accuracy drop is typical and acceptable for most use cases.
Q: What's the difference between Colibri and just using Ollama? A: Colibri is a minimal, educational wrapper that exposes the internals. Ollama is a polished, production-friendly tool that abstracts away the details. If you want to learn how the memory management works, study Colibri. If you just want it to work, use Ollama or LM Studio.
Q: Can I use multiple GPUs?
A: Yes, llama.cpp supports splitting layers across multiple GPUs with --tensor-split. This is often better than CPU offloading if you have two mismatched cards (e.g., a 8 GB and a 6 GB). The framework handles the data movement automatically.
Want to build like a Forward Deployed Engineer?
FDE Coach is a cohort-based program in frontend, backend, AWS, and AI. Build real products and get referred to 200+ hiring partners.
Explore the program