ef is the dynamic candidate list size at query time
ef is the size of the dynamic candidate list maintained during the layer-0 best-first search. It is the single most important query-time knob in HNSW because it lets you trade recall for latency per request without touching the index. During search, the algorithm maintains two structures: a frontier of unexplored candidates ordered by distance, and a result set of the best nodes found so far, both bounded by ef. Larger ef means the search explores more of the graph before it terminates, which reduces the probability of missing a true nearest neighbor, at the cost of more distance computations. The result set is what gets returned as top-k, so ef must be at least as large as limit; Qdrant silently raises it to max(ef, limit) if you set it lower.
The reason this is the right knob to reach for is that it decouples recall from index structure. m and ef_construct are baked into the graph and changing them requires rebuilding; ef is evaluated fresh on every query and has essentially linear cost in distance computations and roughly linear cost in latency at a fixed vector size. That means you can have different ef for different endpoints: a search-as-you-type endpoint uses ef=32 for 3ms responses, while a nightly bulk scoring job uses ef=512 for maximum recall. The same collection serves both. The shape of the recall vs ef curve is concave and dataset-dependent: on most real datasets recall rises steeply between ef=16 and ef=128, then flattens. The knee of that curve is where you want your default; past it you are paying latency for fractions of a point of recall. You should measure this curve on your own data against exact ground truth, not assume the knee is at the same place as a blog post.
ef is per-query and does not require a rebuild, unlike m and ef_construct.
Effective ef is max(hnsw_ef, limit); setting it below limit has no effect.
Cost scales roughly linearly with ef in distance computations, but wall-clock latency can scale worse if you are already CPU-bound or memory-bandwidth-bound.
For quantized collections, ef interacts with oversampling and rescoring: a high ef with binary quantization can still underperform a moderate ef with full-precision rescoring.
The main alternative to raising ef is to improve the graph itself (higher m or ef_construct) at the cost of memory and a rebuild. If you have already rebuilt recently and memory is not the constraint, raising ef is almost always the cheaper move because it is reversible and per-request. The common mistake is setting ef very high to fix a recall problem that is actually caused by the embedding model or by aggressive payload filtering, not by HNSW. Another common mistake is assuming a higher ef always means higher latency - it does in aggregate, but for very small collections that fit in cache the difference can be in the microseconds, so it is worth measuring rather than assuming. A subtler one: on a multi-tenant collection, a single global ef can be wrong for both small and large tenants; if you cannot tune per tenant, a moderate ef with post-filtering on top can be better than a very high ef with heavy pre-filtering. Version note: the exact default of hnsw_ef has changed across releases and is not the same as limit - always set it explicitly in performance-critical paths.
Version-dependent note: query_points with SearchParams(hnsw_ef=...) is the qdrant-client 1.10+ API. On older clients the same parameter was passed to search() as search_params=SearchParams(hnsw_ef=...). The semantics are identical, but the call site differs, so pin your client version in your lockfile and do not copy snippets across major versions without checking.
You set hnsw_ef=5 on a collection and limit=10. Explain why the results are not obviously worse and what Qdrant actually did.
A teammate wants to raise ef to 1000 to improve recall on a 100k-vector collection. What would you expect to happen to latency and recall, and would you approve the change?
Your search endpoint has a 25ms p99 SLO. After a traffic spike, p99 is 60ms and you suspect ef is too high. How do you confirm, and how do you reduce latency without dropping recall below the product target?
You run an A/B test where variant A uses ef=64 and variant B uses ef=256. Recall improves by 0.5 points but click-through is flat. What does this tell you about where the real bottleneck is?
Design a per-query adaptive ef scheme that varies ef based on the query's estimated difficulty (e.g. embedding norm, filter cardinality, or a learned predictor). What signals would you use and how would you validate that it does not regress p99?
You have a multi-tenant collection where tenants range from 1k to 50M vectors. Explain why a single global ef is wrong and propose a tenant-aware tuning strategy that does not require per-tenant collections.
You need to guarantee a recall SLA (e.g. recall@10 >= 0.95) with a hard p99 latency SLA. Describe a system that enforces both at runtime, including what happens when a query cannot satisfy both and how you decide which SLA to violate.
Explain how you would build a closed-loop controller that adjusts ef per collection based on observed recall against sampled exact ground truth, and what stability problems you would expect from such a controller.