How I sped up HNSW construction ~3x #22
orneryd
started this conversation in
Show and tell
Replies: 1 comment
-
|
This is a fascinating deep dive! 🚀 Reducing detours by using a seed-first construction is such an elegant optimization. I've often seen HNSW bottlenecks in vector DBs, so a 3x speedup is massive. Thanks for sharing the detailed breakdown and the Dev.to link! |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
HNSW Build Time at 1M Embeddings: 27 Minutes to 10 Minutes by Fixing Insertion Order
For a 1M-embedding corpus, we reduced HNSW construction time from about 27 minutes to about 10 minutes (2.7x) without changing recall or graph quality.
This post explains:
All numbers in this writeup use the validated parameters from the current implementation:
M=16ef_construction=100256 * 8 = 2,048nodesProblem: Random insertion order creates traversal waste
HNSW build quality and build cost both depend on insertion order. With random insertion:
That wasted traversal work compounds over time.
Visual A: Where random-order traversal waste comes from
In practice, this increases construction cost by a multiplicative overhead factor. I will call that factor
beta:Where:
ideal_timeis the minimum cost if each insert reaches good neighbors with minimal detours,beta > 1captures wasted traversal and repair work.Baseline mechanics: why layer-0 dominates at 1M scale
Using
M=16, the level distribution gives:P(node at layer >= 1) = 1/M = 1/16 = 6.25%93.75%of inserted nodes effectively do all meaningful work in layer 0.For
ef_construction=100, expected distance computations per insertion are:(
32above is2*M, the layer-0 connection bound.)So the primary optimization target is not exotic upper-layer behavior; it is reducing layer-0 traversal waste during insertion.
Solution: BM25-seeded insertion creates a backbone first
Instead of random insertion order, we pick a lexically diverse seed set from BM25 and insert those vectors first.
Seed extraction:
NORNICDB_HNSW_LEXICAL_SEED_MAX_TERMS=256,NORNICDB_HNSW_LEXICAL_SEED_PER_TERM=8,256 * 8 = 2,048nodes.Build order:
N - seed_countnodes.This gives the graph a broad early backbone, so later inserts find useful neighbors quickly instead of wandering.
Visual B: Seed-first construction reduces detours
The math check on the 27 -> 10 minute result
Use a conservative distance-op estimate for 1024-dim
float32vectors in Go:160 nsper distance operation in this workload class.Then ideal floor for 1M insertions:
Now map measured times to
beta:This is the key point: the reported speedup is exactly what you expect if seeded order mostly removes traversal waste.
Visual C: Overhead factor (
beta) before vs afterInterpretation:
3.07xthe ideal work,1.13x.Time decomposition for the 1M run
Visual D: Same floor, different overhead
Both runs share the same algorithmic floor; the difference is how much overhead is paid while traversing and wiring the graph.
Why this does not require a recall tradeoff
This change does not reduce
ef_construction,M, or search-time quality knobs. It changes insertion order so the builder spends less effort reaching good neighborhoods.That is why a large build-time gain can occur without reducing recall or graph quality: the graph is built with the same target connectivity constraints, but with less wasted traversal on the way there.
How to reproduce in your environment
M,ef_constructionunchanged).Use ratio as the primary cross-machine signal. Absolute minutes depend on CPU, memory bandwidth, cache behavior, and runtime effects.
Secondary effect: same seed mechanism helps k-means init
The same BM25-derived seed mechanism is also used by
bm25+kmeans++seed mode for centroid initialization. That improves initial centroid spread and typically reduces convergence iterations in the k-means phase.The important architectural detail is reuse: one seed extraction pass supports both HNSW construction order and k-means initialization.
Closing
The 27-to-10 minute result is not a tuning artifact. It is a direct consequence of reducing traversal waste during graph construction:
betafrom about3.07to about1.13.At 1M scale, this is enough to produce a repeatable 2.7x build-time improvement while preserving result quality.
https://dev.to/orneryd/how-i-sped-up-hnsw-construction-27x-2jhn
Beta Was this translation helpful? Give feedback.
All reactions