ML Systems Lab Open interactive version →
Advanced 26 min read ANNHNSWIVF-PQquantizationretrieval

Embeddings + ANN Serving Deep-Dive

HNSW vs IVF-PQ, the recall–latency knob, index build/refresh, quantization

Once retrieval is a dot product between a query embedding and every item embedding, the bottleneck is search, not the model. Exact nearest-neighbor over 100M vectors of dimension 256 is 100M·256 ≈ 25.6 billion multiply-adds per query — that's 51.2 billion FLOPs (each multiply-add counts as 2), and even a well-optimized multi-core CPU sustaining ~50 billion FLOP/s of dot-product throughput needs ≈1 second per query, ~100× over the 10ms budget, before counting the ~100GB of vectors (100M × 256 × 4 bytes) that would have to stream from memory on every query — hopeless in 10ms. Approximate nearest-neighbor (ANN) trades a sliver of recall for two-to-three orders of magnitude speedup.


HNSW builds a navigable graph; IVF-PQ partitions and compresses. HNSW (hierarchical navigable small world) links each vector to a few neighbors across layered graphs, so a query "greedily walks" from an entry point to its neighborhood in ~log(N) hops — very fast, very high recall, but the full float vectors sit in RAM (100M × 256 × 4 bytes ≈ 100GB). IVF-PQ instead clusters vectors into, say, 4096 cells (search only the nearest few), and product-quantizes each vector — splitting 256 dims into 32 sub-vectors, each mapped to one of 256 centroids — so a vector shrinks from 1024 bytes to 32 bytes, a 32× compression that fits 100M vectors in ~3GB.


Recall and latency are one knob, turned at query time. HNSW's `efSearch` (how many candidates to keep on the walk) and IVF's `nprobe` (how many cells to scan) both trade recall for latency continuously: nprobe=8 might hit 0.92 recall at 3ms, nprobe=64 hits 0.99 recall at 12ms. You don't pick "an index" — you pick an operating point on its recall–latency curve, and that point is a product decision (how many good candidates can the funnel afford to lose?).


Build cost and staleness are the operational tax. HNSW graph construction is O(N·log N) and expensive to mutate, so high-churn catalogs favor periodic rebuilds or a small "fresh" index searched alongside the main one. Quantization (PQ) is lossy: the 32× compression that saves RAM also blurs fine distances, costing a few points of recall — acceptable at retrieval (the ranker re-scores anyway) but never in the final ranker.

Key points

Takeaway

ANN serving turns retrieval's dot-product-over-millions into a tunable recall–latency tradeoff: HNSW greedily walks a navigable graph (fast, high-recall, RAM-heavy) while IVF-PQ partitions and product-quantizes vectors (32× smaller, mildly lossy), and in both you pick an operating point (efSearch/nprobe) rather than a fixed index.

Recap

Check your understanding

Q1. Select the two correct statements about indexing 200M 256-dim vectors with only 8GB RAM per serving node.

Q2. Retrieval recall is 0.92 at nprobe=8 and 3ms. The latency budget allows 12ms and the ranker keeps missing relevant items. What's the correct single-knob change?

Q3. A catalog adds and removes thousands of items per minute. Retrieval keeps returning deleted items and missing brand-new ones, even though HNSW recall benchmarks are excellent. What's the underlying issue?

Try it interactively

ML Systems Lab is a free interview-prep platform for ML engineers — work through the full interactive module, quizzes, and drills.

Open ML Systems Lab →