m is graph connectivity, ef_construct is build-time search breadth
m and ef_construct are both build-time parameters and they control two orthogonal things. m is the maximum number of outbound edges each node is allowed to keep on layers above 0; on layer 0 the cap is 2m because edges are stored bidirectionally. It is the graph connectivity, and it directly drives index memory: roughly 2m * 4 bytes per point on layer 0, plus the upper layers. ef_construct is the size of the dynamic candidate list used while inserting a new node. When a new point is added, HNSW runs a search from the entry point down to the new node level with a beam width of ef_construct, then applies the diversity heuristic to pick which of those candidates actually become edges. So m controls how many edges survive, and ef_construct controls how good a pool of candidates those edges are chosen from.
The mechanism matters because it explains why the two parameters are not interchangeable. If you raise m but leave ef_construct small, you are telling the graph to keep more edges but you are only giving it a shallow candidate pool to choose from - the extra edges tend to be redundant near neighbors rather than useful long-range links, so recall improves only marginally. If you raise ef_construct but leave m small, you give the algorithm a better view of the neighborhood at build time but it can only keep a few of those candidates, so the graph quality gain is capped by m. The two parameters work together: ef_construct determines the quality of the decision, m determines how much of that quality you can store. The diversity heuristic is what converts a wider ef_construct into genuinely better navigability, because a wider beam is more likely to surface a distant candidate that survives the heuristic and becomes a long-range edge.
m: higher = denser graph, better recall, more memory, slower build, higher cache pressure at query time. Typical range is 16-64; the Qdrant default is 16.
ef_construct: higher = better graph quality, slower build, no effect on query latency or memory. Typical range is 100-500; the Qdrant default is 100.
Both require a re-index to change. Neither can be tuned per query.
There is a hard trade-off between m and memory: doubling m roughly doubles graph memory on layer 0, which is often the dominant cost at scale.
The trade-off is therefore memory and build time against recall, with no free lunch. My rule of thumb is to set m based on memory budget first, then set ef_construct high enough that the graph is well connected (typically 2-4x m is a reasonable starting point, but measure). The common mistake is assuming ef_construct affects query performance. It does not - it is purely a build-time knob, and if you are trying to fix slow queries by lowering ef_construct you are just going to get a worse index for the same query cost. Another common mistake is setting m very high expecting linear recall gains. In practice recall vs m plateaus: once the graph is connected enough for the intrinsic dimensionality of the data, extra edges add memory and cache misses without improving recall. The alternative to raising m is to raise ef at query time instead, which costs latency but no memory and no rebuild - often the better lever when you cannot afford a re-index. If memory is the binding constraint, IVF-PQ is the alternative index family to consider, trading recall for a much smaller footprint.
Version-dependent: Qdrant has adjusted default m and ef_construct values across releases and the defaults differ by vector size, so read the effective config with client.get_collection() rather than assuming. The update_collection path for hnsw_config has been stable for several releases, but on very large collections the re-index can be effectively an outage, so the production-safe pattern is to create a new collection with the target config and migrate points rather than mutating in place.
You are creating a Qdrant collection for a small demo with 50k vectors. What m and ef_construct do you pick, and why does the choice barely matter at this scale?
A teammate says the index is slow to build because m is too high and wants to lower it. Is that correct, and what else could be responsible for slow builds?
You raise m from 16 to 48 on a 20M-point collection and memory usage triples while recall only goes from 0.91 to 0.93. Diagnose what is happening and propose a better use of that memory budget.
Your team wants to add 5M new points per day to a collection with m=32. Explain how the build cost of HNSW interacts with your ingest rate and what you would change to keep up.
Design an experiment that isolates the effect of m from ef_construct on recall and latency for a specific dataset. What do you measure, how many configurations, and how do you avoid confounding the two?
You are asked to cut index memory by 50 percent without losing more than two points of recall@10. Walk through the levers (m, quantization, on-disk, dimensionality reduction) and the order in which you would try them.
You must build HNSW for a 2B-vector corpus under a strict build-time SLO and a fixed memory budget. Derive how you would choose m and ef_construct analytically from the SLO, and explain where the analytical model breaks down in practice.
A vendor claims their HNSW variant dominates Qdrant at the same m and ef_construct. What would you measure to falsify that claim, and what hidden knobs (heuristic variants, entry point selection, pruning strategy) would you expect them to be exploiting?