Tutti i post

Comprimere i grafi HNSW senza rallentarli

di tusKANNy team
Graph IndexCompressionHNSWkANNolo

I post del blog sono scritti solo in inglese.

Vector search is often powered by proximity graphs such as HNSW (Hierarchical Navigable Small World)1. The edges of these graphs can take a large part of the index memory, especially when the vectors are short or quantized (compressed). In our latest work in collaboration with eBay (CIKM 2026), Lossless Compression of Proximity Graph Indexes for Efficient Approximate Nearest Neighbors Search2, we show how to compress the adjacency lists of HNSW by up to 2.1× without slowing down search. The technique has been running in production at eBay since early 2025, where it reduces the memory taken by the edges by 44.3%, and it is now also available in kANNolo3.

Where the memory goes

HNSW limits the number of neighbors of each node. In our graphs a node has between 30 and 50 neighbors on average, and each of them is stored as a 32-bit ID. When the vectors themselves are small, these IDs end up taking a large share of the index. On some datasets, such as eBay's short product embeddings or the MS MARCO-v14 passages encoded with Snowflake5 and quantized with Product Quantization (PQ)6, the edges take more than half of the memory:

Share of index memory taken by vectors and edges, before and after edge compression. Edges go from 40.3% to 28.2% on MS MARCO-v1 with CoCondenser, from 52.4% to 37.7% on eBayProducts7M, from 57.1% to 41.6% on MS MARCO-v1 with Snowflake and PQ, and from 33.8% to 19.4% on Sift1M.

However, compressing the edges must not slow down search. A graph traversal visits the nodes in an unpredictable order, so each adjacency list has to be decoded on the fly while the search is running, and the decoding time adds directly to the query time.

Making IDs compressible

We evaluated several approaches, including WebGraph7, the well-known framework for compressing web graphs, which encodes a list as a copy of a similar list plus a few extra edges. None of them works well on HNSW graphs, because two adjacency lists usually share very few neighbors. For this reason, we compress each list independently.

The usual way to do this is to sort the list and store the gaps between consecutive IDs. However, node IDs follow the insertion order and have nothing to do with the structure of the graph, so the neighbors of a node are spread over the whole ID space. On eBayProducts7M, bit-packing costs 23 bits per ID and the entropy of the IDs is 22.6 bits, which means that they are close to uniform. Storing gaps instead of IDs only brings the entropy down to 18.5 bits.

To fix this, we renumber the nodes so that nodes appearing in the same lists get close IDs. The permutation is computed offline with Enhanced Graph Bisection (EGB)8, a recursive bisection algorithm designed for reordering indexes. After EGB, the entropy of the gaps on eBayProducts7M drops to 15.0 bits.

The vectors are permuted together with the nodes, so the search runs entirely in the new ID space and only the final results are mapped back to the original IDs. Storing the inverse permutation needed for this mapping costs ⌈log₂ N⌉ bits per vector. Reordering also improves cache locality9: on eBayProducts7M, it alone makes the uncompressed index 17% faster.

Choosing the codec

Bits per edge on eBayProducts7M, counting the adjacency lists alone, without the offsets and the inverse permutation. Bit-packing costs 23.0 bits, the entropy of the IDs is 22.6, the entropy of the gaps is 18.5 in the original order and 15.0 after EGB. On the EGB-ordered gaps Zeta codes use 16.5 bits per edge and Stream VByte 17.5.

We compared several integer codecs on the reordered gaps (the plot above counts the adjacency lists alone, without the offsets and the inverse permutation). Zeta codes10 come closest to the entropy. Stream VByte11 uses about one bit more per edge, but it is byte-aligned and stores the byte lengths of four integers in a separate control byte, so it can be decoded with SIMD (Single Instruction, Multiple Data) instructions and without branches. Since each adjacency list is encoded independently, the search only decodes the lists of the nodes it actually visits.

The offsets that mark where each list starts are compressed with Elias-Fano12. They take about 8 bits per node instead of 64, and any offset can still be accessed directly.

Results

We report results on two datasets: eBayProducts7M, a collection of 7.3M 96-dimensional product embeddings from eBay, and MS MARCO-v1 encoded with Snowflake. The Snowflake vectors are compressed with PQ, and the retrieved candidates are reranked on the full vectors with a RerankIndex13. The paper also reports results on Sift1M and on MS MARCO-v1 encoded with the sparse CoCondenser14 model.

The plot below shows the space taken by each method in bits per edge, including the offsets and, for the reordered graphs, the inverse permutation. Under each value is the compression ratio with respect to the uncompressed graph (Plain):

Bits per edge of each method. On eBayProducts7M: Plain 33.3, Bit-packing 23.1 (1.4×), Elias-Fano 23.2 (1.4×), Zeta 17.4 (1.9×), Stream VByte 18.3 (1.8×). On MS MARCO-v1 with Snowflake (PQ): Plain 33.6, Bit-packing 24.1 (1.3×), Elias-Fano 23.9 (1.4×), Zeta 17.0 (1.9×), Stream VByte 18.0 (1.8×).

The table reports the average query latency in µs at 90 / 94 / 98% Accuracy@10:

eBayProducts7MMS MARCO-v1 Snowflake (PQ)
Plain185 / 264 / 632261 / 302 / 497
Bit-packing193 / 277 / 668266 / 310 / 509
Elias-Fano231 / 333 / 832288 / 338 / 563
Zeta214 / 302 / 714277 / 324 / 545
Stream VByte177 / 250 / 564260 / 302 / 494

Zeta compresses the most, but it is 13–16% slower than Plain on eBayProducts7M and 6–10% slower on MS MARCO-v1 Snowflake. Elias-Fano is the slowest method and does not compress better than bit-packing. Stream VByte takes only about one bit per edge more than Zeta and is as fast as Plain or faster. On eBayProducts7M it is even 11% faster at 98% accuracy, thanks to the better locality of the reordered graph. The other two datasets in the paper show the same trend, with only a small overhead on Sift1M.

Computing the permutation adds 7.8% (MS MARCO-v1 Snowflake) and 15% (eBayProducts7M) to the build time, and it is a one-time offline cost.

In production at eBay

eBay's search engine, Cassini15, uses HNSW for vector retrieval. This compression framework has been part of its production pipeline since early 2025. The process is the same as the one described above, except that the permutation is computed with KaMinPar16, a k-way graph partitioner, instead of EGB. At identical recall, the edges of the HNSW graph take 44.3% less memory, with no measurable latency degradation under live traffic. The memory saved allows eBay to use HNSW in more search use cases.

Try it in kANNolo

Graph compression is a single argument on every HNSW index in kANNolo (pip install kannolo). Search results are identical to those of the uncompressed index, and IDs are the original ones:

import numpy as np
from kannolo import DensePlainHNSW
 
data = np.load("embeddings.npy").astype(np.float32)  # shape (n, 1024)
query = data[0]
 
index = DensePlainHNSW.build_from_array(
    data.flatten(), dim=1024,
    m=32, ef_construction=200, metric="dotproduct",
    graph_type="compressed",  # EGB permutation + Stream VByte
)
index.save("index.bin")
 
# The file does not record the graph type, so pass it again when loading.
index = DensePlainHNSW.load("index.bin", metric="dotproduct", graph_type="compressed")
dists, ids = index.search(query, k=10, ef_search=100)

graph_type="permuted" applies only the reordering, for faster queries without the space savings. The same option is available on the sparse and PQ indexes and in the hnsw_build binary (--graph-type streamvbyte). The graph compression guide has the details, and the code to reproduce the paper is in our reproducibility repository.

References

  1. Yu A. Malkov, D. A. Yashunin. Efficient and Robust Approximate Nearest Neighbor Search Using Hierarchical Navigable Small World Graphs. IEEE Transactions on Pattern Analysis and Machine Intelligence, 2020. doi:10.1109/TPAMI.2018.2889473 ↩

  2. Francesco Benocci, Leonard Dahlmann, Francesco Gargiulo, Tobias Heuer, Silvio Martinico, Franco Maria Nardini, Cosimo Rulli, Rossano Venturini. Lossless Compression of Proximity Graph Indexes for Efficient Approximate Nearest Neighbors Search. CIKM, 2026. doi:10.1145/3799682.3840116 ↩

  3. Leonardo Delfino, Domenico Erriquez, Silvio Martinico, Franco Maria Nardini, Cosimo Rulli, Rossano Venturini. kANNolo: Sweet and Smooth Approximate k-Nearest Neighbors Search. ECIR, 2025. doi:10.1007/978-3-031-88717-8_29 ↩

  4. Tri Nguyen, Mir Rosenberg, Xia Song, Jianfeng Gao, Saurabh Tiwary, Rangan Majumder, Li Deng. MS MARCO: A Human Generated MAchine Reading COmprehension Dataset. CoCo Workshop at NeurIPS, 2016. ceur-ws.org ↩

  5. Puxuan Yu, Luke Merrick, Gaurav Nuti, Daniel Campos. Arctic-Embed 2.0: Multilingual Retrieval Without Compromise. arXiv, 2024. arXiv:2412.04506 ↩

  6. Hervé Jégou, Matthijs Douze, Cordelia Schmid. Product Quantization for Nearest Neighbor Search. IEEE Transactions on Pattern Analysis and Machine Intelligence, 2011. doi:10.1109/TPAMI.2010.57 ↩

  7. Paolo Boldi, Sebastiano Vigna. The WebGraph Framework I: Compression Techniques. WWW, 2004. doi:10.1145/988672.988752 ↩

  8. Joel Mackenzie, Matthias Petri, Alistair Moffat. Faster Index Reordering with Bipartite Graph Partitioning. SIGIR, 2021. doi:10.1145/3404835.3462991 ↩

  9. Benjamin Coleman, Santiago Segarra, Alex Smola, Anshumali Shrivastava. Graph Reordering for Cache-Efficient Near Neighbor Search. NeurIPS, 2022. arXiv:2104.03221 ↩

  10. Paolo Boldi, Sebastiano Vigna. Codes for the World Wide Web. Internet Mathematics, 2005. doi:10.1080/15427951.2005.10129113 ↩

  11. Daniel Lemire, Nathan Kurz, Christoph Rupp. Stream VByte: Faster Byte-Oriented Integer Compression. Information Processing Letters, 2018. doi:10.1016/j.ipl.2017.09.011 ↩

  12. Peter Elias. Efficient Storage and Retrieval by Content and Address of Static Files. Journal of the ACM, 1974. doi:10.1145/321812.321820 ↩

  13. Silvio Martinico, Franco Maria Nardini, Cosimo Rulli, Rossano Venturini. Multivector Reranking in the Era of Strong First-Stage Retrievers. ECIR, 2026. doi:10.1007/978-3-032-21324-2_4 ↩

  14. Thibault Formal, Carlos Lassance, Benjamin Piwowarski, Stéphane Clinchant. From Distillation to Hard Negative Sampling: Making Sparse Neural IR Models More Effective. SIGIR, 2022. doi:10.1145/3477495.3531857 ↩

  15. Andrew Trotman, Jon Degenhardt, Surya Kallumadi. The Architecture of eBay Search. eCOM Workshop at SIGIR, 2017. ceur-ws.org ↩

  16. Lars Gottesbüren, Tobias Heuer, Peter Sanders, Christian Schulz, Daniel Seemaier. Deep Multilevel Graph Partitioning. ESA, 2021. doi:10.4230/LIPIcs.ESA.2021.48 ↩