Express lanes — layers and ef_search

HNSW stacks skip-list-style layers — a few nodes promoted to sparse upper layers act as express lanes that cross huge distances in one hop before the search descends to the dense bottom layer for fine refinement — making search roughly logarithmic, while ef_search trades a wider, slower candidate beam for more recall until it plateaus.

Previously

A flat graph hops one short link at a time; HNSW's promoted upper layers are express lanes that cross the space in a few big hops, then descend for precise local steps — roughly logarithmic search, with ef_search as the recall-vs-latency dial. But all that speed and recall has a hidden bill: HNSW keeps every full vector AND every edge in memory.

Scene 07

Express lanes — layers and ef_search

  1. Watch
  2. Try it
  3. Predict
  4. Capture
L2L1L0 (base)acoustic → electroniccalm → energeticLo-fi RainAcoustic Suns…Campfire FolkCoffeehouseIndie DriveSynth DawnNeon CityClub PulseRave PeakBass DropMidnight DriveGarage BeatStudy BeatsLonely SynthNow PlayingTop layer: a few promoted nodes with long edges. Bottom layer: every node, de…RECALL vs LATENCYrecallslower →FlatIVFHNSW
What to watch for

Last scene the graph was flat — one short hop at a time. Picture searching a billion points that way: from a random start you'd take thousands of tiny steps just to cross the map. The fix is borrowed straight from a skip list. Most songs live only on the BOTTOM layer with their close local links. But a few are PROMOTED to a sparse TOP layer and joined by long edges — think of an express train that skips every local stop, gets you across town in one ride, and then you switch to the local for the last block. Watch the search enter at the top, take one big express hop into the query's region, drop down a layer, and finish with small careful hops — landing on Midnight Drive, Synth Dawn, and Lonely Synth, the query's true neighbors.

Continue unlocks when the animation finishes.
Implementation

Highlighted lines are the ones running in the diagram right now.

HNSW.search
enter at the top, descend layer by layer to the query
def search(query, k):
ep = entry_point # a promoted top-layer node
# express lanes: greedy-hop down the sparse upper layers,
# keeping just the single best node at each level
for level in range(top_layer, 0, -1):
ep = search_layer(query, ep, ef=1, level)
# bottom layer: dense local refinement with the full beam
candidates = search_layer(query, ep, ef=ef_search, level=0)
return candidates.nearest(k)
HNSW.searchLayer
greedy best-first walk with a beam of width ef
def search_layer(query, ep, ef, level):
visited = {ep}
candidates = pq([ep]) # frontier to explore
found = pq([ep]) # best ef so far
while candidates:
c = candidates.pop_nearest(query)
if dist(c, query) > found.farthest_dist():
break # nothing closer left to find
for n in neighbors(c, level):
if n not in visited:
visited.add(n)
candidates.push(n); found.push(n)
found.trim_to(ef) # keep only ef closest
return found
HNSW.insert
exponential decay promotes a few nodes — the express lanes
def insert(node):
# most nodes stay at level 0; a rare few are promoted high,
# which is what builds the sparse upper express lanes
level = floor(-ln(rand()) * mL)
ep = entry_point
for lvl in range(top_layer, level, -1):
ep = search_layer(node, ep, ef=1, lvl)
for lvl in range(min(level, top_layer), -1, -1):
nbrs = search_layer(node, ep, ef=ef_construction, lvl)
node.link(select_M(nbrs), lvl) # M edges per level
if level > top_layer: entry_point = node

Where this sits in Build a vector database (Pinecone / Weaviate / pgvector style)

Scene 07 of 15, in the The graph act — HNSW: hop a proximity graph; express-lane layers and the ef_search knee.. Skip-list layers add express-lane nodes that cross huge distances in one hop, then descend for local refinement — roughly log search, with ef_search trading beam width for recall until it plateaus.

Up next. HNSW won the speed-and-recall corner, but it pays in RAM — full float32 vectors plus a graph of edges. At a billion 1536-dim vectors that's the 6 TB wall from scene 1, back again. So the next question is: can we shrink each vector from 6 KB to a hundred bytes without wrecking recall?

All 15 scenes in Build a vector database (Pinecone / Weaviate / pgvector style) · Every curriculum

Built with Arqly
Every scene in Build a vector database (Pinecone / Weaviate / pgvector style) builds on the one before it.All 15 Build a vector database (Pinecone / Weaviate / pgvector style) scenes