v5.0
live available
ai-architecture guide 11 Oct 2026

A hundred thousand vectors, six HNSW settings, and the one that beat the biggest

The largest index in the sweep cost 6.6x the build time, 23 percent more memory, and answered queries 1.9x slower than a smaller one, for four hundredths of a percent more recall.

By Purvansh Parmar Checked 2026-10-21 Read 8 min Entries 7

Every HNSW tutorial gives you three knobs and no way to decide between them. M controls how many neighbours each node keeps, efConstruction how hard the builder works, and ef how wide the search looks. The usual advice is that bigger is better and slower, which is half right in a way that costs real money: in this sweep the largest index was slower to query than one a quarter of its build cost, and matched it on recall to four decimal places. Bigger was not a trade. It was just worse.

How this was checkedBuilt six HNSW indexes over the same 100,000 clustered 384 dimensional vectors in faiss 1.15.1, scored every one against exact inner product ground truth at five different search widths, and timed single threaded queries. The biggest configuration lost to a much smaller one on latency while matching it on recall. Exact brute force over the whole set answered in 2.53 milliseconds per query, which is the number that decides whether any of this is worth doing at a hundred thousand vectors.

What I compared on

01 No index, exact scan

A matrix multiply against every vector, which is what the index is supposed to save you from.

Wins
It is correct, it has nothing to tune, and at this size it is fast enough for a great many products. Anything that answers in under three milliseconds is not the bottleneck in a system that then calls a language model.
Loses
It is linear in corpus size, so the 2.53 milliseconds becomes 25 at a million vectors and 250 at ten million. The index exists for the corpus you will have, not the one you have.
2.53 ms per query154 MBrecall 1.0

02 M 16, efConstruction 100

The middle setting, and the one this sweep says to use: modest degree, modest build effort.

Wins
It matches every larger configuration on recall and beats all of them on latency. Against M 64 it is 6.6x cheaper to build, 19 percent smaller, and 1.9x faster per query at the same search width.
Loses
At a narrow search width it is visibly weaker than the high degree indexes: recall 0.84 at ef 16 against 0.9146 for M 64. If you are forced to run a very narrow search, degree starts to matter again.
8.3 s build0.194 ms at recall 0.9996168 MB

03 M 8, efConstruction 100

The cheapest graph here, half the neighbours per node of the recommended default.

Wins
It is the right answer when memory is the binding constraint, and its query times are the lowest in the table: 0.059 ms at ef 16 is the fastest number anywhere in this sweep.
Loses
It is the only configuration that never quite reaches the others: 0.9958 at ef 256 against 1.0 for the mid range indexes at ef 128, so the last half percent of recall is not available at any search width.
7.6 s build161.7 MBrecall caps at 0.9958

04 M 16, efConstruction 200

The same graph degree with twice the build effort, the usual advice for squeezing out more recall.

Wins
Doubling build effort is the one lever that costs no memory and no query time at all: the index is byte for byte the same size and the queries run at the same speed.
Loses
It doubles the build for a gain that disappears as soon as you widen the search slightly. The same recall is available from the cheaper build by moving ef from 64 to 128, which costs 0.043 ms.
16.8 s build2x build time0.0126 recall gained at ef 16

05 M 32, efConstruction 200

Double the graph degree, the first setting where the index gets meaningfully larger than the vectors.

Wins
It is the strongest of the lot at narrow search widths, holding recall 0.901 at ef 16, which matters if your latency budget forces a small ef.
Loses
You pay 12.8 MB and 28 percent more query latency for two ten thousandths of recall. At ordinary search widths that is a straight loss against M 16.
20.2 s build180.8 MB0.249 ms at recall 1.0

06 M 48, efConstruction 200

Three times the default degree, the point where the graph itself is a third of the payload.

Wins
It is marginally better than M 32 at the narrowest width, 0.9134 against 0.901, which is the only axis on which raising degree this far shows up at all.
Loses
39.6 MB more than the raw vectors, and every query is slower than the same recall costs on M 16. It is the configuration people pick because it sounds safe.
21.6 s build193.6 MB0.286 ms at recall 1.0

07 M 64, efConstruction 400

The maximum setting in the sweep, built the way a cautious team sizes an index it cannot easily rebuild.

Wins
Nothing here. The one figure in its favour is recall 0.9146 at ef 16, and even that is beaten by M 48 at a lower build cost.
Loses
It loses on build time, on memory, and on latency at every search width, and its recall advantage vanishes by ef 32. This is the configuration the sweep exists to talk you out of.
55.2 s build206.4 MB0.364 ms, 1.9x slower than M 16

Side by side

BuildSizeBest recallms at that recall
No index0 s154 MB1.02.53
M 8, efC 1007.6 s161.7 MB0.99580.261
M 16, efC 1008.3 s168.0 MB0.99960.194
M 16, efC 20016.8 s168.0 MB1.00.291
M 32, efC 20020.2 s180.8 MB1.00.249
M 48, efC 20021.6 s193.6 MB1.00.286
M 64, efC 40055.2 s206.4 MB1.00.364

Verdict

Build at M 16, efConstruction 100, and do the rest of your tuning with ef at query time. That single index went from recall 0.84 to 0.9996 without being rebuilt once, just by widening the search from 16 to 128, and it answered faster at full recall than every larger index in the sweep. The knob worth turning is the one that does not require a rebuild. And before any of it, check what an exact scan costs: at a hundred thousand vectors it was 2.53 ms, so the index here is buying latency, not feasibility, and it is worth knowing which one you are shopping for.

Sources