Billion-scale vector search in GCP: The mathematics of ScaNN, HNSW, and anisotropic quantization

A comprehensive, research-backed engineering deep-dive into the mathematics, backend architecture, and productionization of Vertex AI Vector Search.


In the modern Generative AI stack, generating a high-quality embedding is only half the battle. Once you have embedded your enterprise’s 10 million PDFs, 50 million product images, and 1 billion user logs into a 768-dimensional hypersphere, you must retrieve them.

If you attempt to calculate the exact mathematical similarity between a user’s query and 1 billion vectors, your system will crash, your latency will be measured in minutes, and your compute costs will bankrupt your project.

To solve this, Google Cloud’s Vertex AI Vector Search (formerly Matching Engine) utilizes Approximate Nearest Neighbors (ANN). Specifically, it relies on a Google Research breakthrough called ScaNN (Scalable Nearest Neighbors), which fundamentally rewrote the physics of vector compression using Anisotropic Vector Quantization.

This is the all-out, diamond-quality guide to the lifecycle of billion-scale vector retrieval on GCP. We cover the physics of the Curse of Dimensionality, the spatial routing of HNSW graphs, the mathematics of ScaNN, Vertex AI deployment architecture, and an in-depth Mathematical Sandbox (Appendix) containing 6 rigorous calculations proving the physics behind the scenes.


1. The physics of the problem: The curse of dimensionality

To understand why we need ScaNN, we must understand why exact search fails.

Exact search is called K-Nearest Neighbors (KNN). To find the closest document to a query, you must calculate the dot product between the query vector \mathbf{q} and every document vector \mathbf{x}_i in your database of size N.

\text{Time Complexity} = \mathcal{O}(N \cdot d)

If N = 1,000,000,000 (1 billion vectors) and d = 768 (dimensions), a single user query requires 768 Billion floating-point operations (FLOPs). If you have 1,000 concurrent users, your system requires 768 Trillion FLOPs per second just to do basic retrieval. Furthermore, storing 1 billion 768-D FP32 vectors requires ~3 Terabytes of RAM.

This is physically and economically impossible for real-time RAG. We must trade a tiny fraction of accuracy (Recall) for massive gains in speed and compression.


2. Spatial routing: The HNSW graph

The first step to avoiding \mathcal{O}(N) complexity is to stop searching the entire database. Vertex AI utilizes HNSW (Hierarchical Navigable Small World) graphs to achieve \mathcal{O}(\log N) search time.

The mathematics of skip lists in high dimensions

HNSW builds a multi-layered graph.

  • Layer 0 (the bottom layer) contains every single vector in your database, connected to its closest neighbors.
  • Layer 1 contains a random subset of those vectors.
  • Layer L (the top layer) contains only a handful of entry-point vectors.

The probability that a vector is inserted into layer l is governed by an exponentially decaying probability function:

P(l) = \lfloor -\ln(\text{Uniform}(0,1)) \cdot m_L \rfloor

(Where m_L is a normalization constant).

The physics of the search: When a query arrives, it enters the top layer. It greedily hops to the connected node that is mathematically closest to the query. Once it hits a local minimum, it drops down to the next layer and repeats. It “zooms in” on the correct neighborhood in logarithmic time, completely ignoring 99% of the database. (See Appendix: Example 2 for the HNSW Routing Math).


3. Google’s breakthrough: Vector Quantization (VQ)

HNSW solves the time complexity, but it does not solve the space (RAM) complexity. To fit 1 billion vectors into memory, we must compress them using Vector Quantization (VQ).

Standard Asymmetric Hashing (AH)

Instead of storing a 768-D vector of 32-bit floats, we divide the vector into M sub-vectors (e.g., 4 blocks of 192 dimensions).
For each block, we run K-Means clustering to find 256 “centroids” (representative points). We replace the actual sub-vector with the 8-bit integer ID (0-255) of its closest centroid.

\mathbf{x} \approx [c_{1}, c_{2}, c_{3}, c_{4}]

This compresses the vector from 3072 bytes down to just 4 bytes.
During search, we pre-compute the distance between the query and the 256 centroids, storing them in a Lookup Table (LUT). We can now calculate distances using \mathcal{O}(1) array lookups instead of \mathcal{O}(d) math. (See Appendix: Example 4 for the LUT Math).


4. The genius of ScaNN: Anisotropic vector quantization

Standard VQ has a fatal flaw. K-Means clustering minimizes the Euclidean distance between the original vector \mathbf{x} and the centroid \tilde{\mathbf{x}}:

\mathcal{L}_{standard} = ||\mathbf{x} - \tilde{\mathbf{x}}||^2

But in RAG, we don’t care about Euclidean distance! We care about the Maximum Inner Product Search (MIPS)—the dot product \langle \mathbf{q}, \mathbf{x} \rangle.

In 2020, Google Research published the ScaNN paper, proving that standard VQ destroys the relative ranking of dot products. If the quantization error shifts the vector parallel to its original direction, it drastically alters the magnitude of the dot product. If the error shifts the vector orthogonally (perpendicularly), the dot product remains relatively stable.

The anisotropic loss function

Google rewrote the quantization loss function to penalize parallel error heavily, while forgiving orthogonal error. They decomposed the error into parallel (||) and orthogonal (\perp) components:

\mathcal{L}_{anisotropic} = ||\mathbf{x}_{||} - \tilde{\mathbf{x}}_{||}||^2 + w ||\mathbf{x}_{\perp} - \tilde{\mathbf{x}}_{\perp}||^2

Where w < 1 is the weight applied to the orthogonal error.

The physics: ScaNN intentionally chooses centroids that might be further away in pure Euclidean space, but which perfectly preserve the original vector’s angle and magnitude relative to potential queries. This mathematical trick is why Vertex AI Vector Search achieves 90%+ Recall at speeds where competitors drop to 70%. (See Appendix: Example 3 for the rigorous Anisotropic proof).


5. GCP architecture: Vertex AI Vector Search

Google Cloud packages HNSW and ScaNN (Tree-AH) into a fully managed, auto-scaling infrastructure called Vertex AI Vector Search.

The production architecture

  1. Index: The mathematical data structure (ScaNN or HNSW) built from your GCS bucket of vectors.
  2. Index Endpoint: The deployed compute resources (nodes) that host the Index in memory.
  3. Sharding: If your index exceeds the memory of a single node, GCP automatically shards the HNSW graph across multiple machines. A query is broadcast to all shards (Scatter), and the results are aggregated (Gather).
  4. Streaming Updates: Vertex AI allows you to insert, update, or delete vectors in real-time without rebuilding the entire index, utilizing a dynamic in-memory buffer that periodically flushes to the static ScaNN graph.


6. Engineering implementation: Python SDK workflow

Here is the exact code to create a ScaNN-based (Tree-AH) index and query it using the aiplatform SDK.

from google.cloud import aiplatform

aiplatform.init(project="your-project-id", location="us-central1")

# 1. Create the ScaNN (Tree-AH) Index
# This triggers a backend job to run Anisotropic Quantization on your vectors
my_index = aiplatform.MatchingEngineIndex.create_tree_ah_index(
    display_name="enterprise-scann-index",
    contents_delta_uri="gs://your-bucket/embeddings/",
    dimensions=768,
    approximate_neighbors_count=150, # How many neighbors to retrieve during search
    distance_measure_type="DOT_PRODUCT_DISTANCE",
    leaf_node_embedding_count=500, # ScaNN specific: Number of embeddings per leaf
    leaf_nodes_to_search_percent=7, # ScaNN specific: % of leaves to search (Speed vs Recall tradeoff)
)

# 2. Create an Endpoint and Deploy the Index
my_index_endpoint = aiplatform.MatchingEngineIndexEndpoint.create(
    display_name="scann-endpoint",
    public_endpoint_enabled=True
)
my_index_endpoint.deploy_index(index=my_index, deployed_index_id="scann_deployed_1")

# 3. Query the Index in Real-Time (< 10ms)
query_vector = [0.01, 0.05, -0.02, ...] # Your 768-D query from gemini-embedding-001

response = my_index_endpoint.find_neighbors(
    deployed_index_id="scann_deployed_1",
    queries=[query_vector],
    num_neighbors=10
)

for neighbor in response[0]:
    print(f"Doc ID: {neighbor.id}, Distance: {neighbor.distance}")

7. Measuring success: The Pareto frontier (Recall vs. QPS)

In Vector Search, you are always trading accuracy for speed. You measure this using the Pareto Frontier curve, plotting Recall@10 (Y-axis) against Queries Per Second (QPS) (X-axis).

  • Recall@10: Out of the true top 10 closest vectors (if we did exact math), how many did our ScaNN index actually find?
  • QPS: How many queries can the system handle per second?

By adjusting the leaf_nodes_to_search_percent parameter in Vertex AI, you slide along this curve. Searching 10% of leaves might yield 95% Recall at 1,000 QPS. Searching 2% of leaves might yield 85% Recall at 5,000 QPS. (See Appendix: Example 6).


8. Valid references & citation links

  1. ScaNN (Anisotropic Quantization): Guo, R., et al. (2020). Accelerating Large-Scale Inference with Anisotropic Vector Quantization. Google Research. arXiv:1908.10396
  2. HNSW Graph Math: Malkov, Y. A., & Yashunin, D. A. (2018). Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs. arXiv:1603.09320
  3. Product Quantization: Jégou, H., et al. (2010). Product Quantization for Nearest Neighbor Search. IEEE TPAMI.
  4. Vertex AI Vector Search Docs: Google Cloud. (2026). Vector Search Overview. Google Cloud Architecture Center. View Documentation


APPENDIX: The mathematical sandbox

This section contains 6 rigorous, research-level mathematical calculations proving the physics of billion-scale vector search on GCP.

Example 1: The exact KNN latency calculation

Why is exact search impossible? Let’s calculate the compute time for 1 Billion vectors.

  • Database N = 10^9 vectors.
  • Dimensions d = 768.
  • A dot product requires d multiplications and d-1 additions \approx 2d FLOPs.
  • Total FLOPs per query = 10^9 \times (2 \times 768) = \mathbf{1.536 \times 10^{12} \text{ FLOPs (1.5 TeraFLOPs)}}.

If a standard CPU core processes 50 GigaFLOPs per second, a single query would take 1536 / 50 = \mathbf{30.7 \text{ seconds}}. For a web app with 100 concurrent users, latency spikes to nearly an hour. We must use ANN.


Example 2: HNSW layer probability math

How does HNSW decide which vectors go to the top layer? It uses a geometric distribution.

P(l) = \lfloor -\ln(\text{Uniform}(0,1)) \cdot m_L \rfloor

Assume the level multiplier m_L = 0.5. We generate a random number U = 0.04.

  1. -\ln(0.04) = 3.218
  2. 3.218 \times 0.5 = 1.609
  3. \lfloor 1.609 \rfloor = \mathbf{1}

This specific vector will be inserted into Layer 0 and Layer 1, but not Layer 2. Because -\ln(U) produces exponentially fewer large numbers, the top layers remain incredibly sparse, creating the perfect “highway” for fast spatial routing.


Example 3: Anisotropic vs. isotropic quantization (The ScaNN Proof)

Let’s prove why Google’s ScaNN preserves dot products better than standard K-Means.
Assume a 2D space.

  • Original Vector \mathbf{x} = [1.0, 0.0]
  • Query Vector \mathbf{q} = [1.0, 0.0] (The query is perfectly aligned with \mathbf{x}).
  • True Dot Product \langle \mathbf{q}, \mathbf{x} \rangle = (1.0 \times 1.0) + (0.0 \times 0.0) = \mathbf{1.0}.

We have two possible centroids to compress \mathbf{x} into:

  • Centroid A (Orthogonal Error): \tilde{\mathbf{x}}_A = [1.0, 0.2]. (Error is perpendicular to \mathbf{x}).
  • Centroid B (Parallel Error): \tilde{\mathbf{x}}_B = [0.8, 0.0]. (Error is parallel to \mathbf{x}).

Standard K-Means (Euclidean Distance):

  • Distance to A: \sqrt{(1.0-1.0)^2 + (0.0-0.2)^2} = \mathbf{0.2}
  • Distance to B: \sqrt{(1.0-0.8)^2 + (0.0-0.0)^2} = \mathbf{0.2}
    Standard K-Means sees them as equally good and might pick B.

The Dot Product reality (MIPS):

  • Dot Product with A: \langle \mathbf{q}, \tilde{\mathbf{x}}_A \rangle = (1.0 \times 1.0) + (0.0 \times 0.2) = \mathbf{1.0}. (Perfectly preserves the true score!)
  • Dot Product with B: \langle \mathbf{q}, \tilde{\mathbf{x}}_B \rangle = (1.0 \times 0.8) + (0.0 \times 0.0) = \mathbf{0.8}. (Destroys the score!)

Conclusion: ScaNN’s Anisotropic loss mathematically penalizes Centroid B, forcing the system to choose Centroid A. This preserves the ranking order of the database.


Example 4: Asymmetric Hashing (AH) lookup math

How does AH achieve \mathcal{O}(1) distance computation?
Assume a 4-dimensional vector \mathbf{x} = [0.5, 0.2, 0.8, 0.1]. We split it into 2 blocks of 2 dimensions.

  • Block 1: [0.5, 0.2] is quantized to Centroid ID 12.
  • Block 2: [0.8, 0.1] is quantized to Centroid ID 85.
  • Compressed \mathbf{x} in RAM: [12, 85].

When Query \mathbf{q} = [0.4, 0.3, 0.9, 0.0] arrives:

  1. We split \mathbf{q} into [0.4, 0.3] and [0.9, 0.0].
  2. We calculate the distance between [0.4, 0.3] and all 256 possible centroids for Block 1. We store this in a Lookup Table (LUT).
  3. We do the same for Block 2.

To find the distance between \mathbf{q} and \mathbf{x}, we do zero math. We simply look up:

\text{Distance} = \text{LUT}_1[12] + \text{LUT}_2[85]

Two array lookups replace all floating-point arithmetic, accelerating search by 100\times.


Example 5: Memory footprint compression

Let’s calculate the RAM required for 1 Billion 768-D vectors using ScaNN.

  • Uncompressed: 10^9 \times 768 \times 4 \text{ bytes (FP32)} = \mathbf{3.07 \text{ Terabytes}}.
  • ScaNN Compression: We split the 768 dimensions into 192 blocks of 4 dimensions. Each block is quantized to an 8-bit integer (1 byte).
  • Compressed Vector Size: 192 \times 1 \text{ byte} = \mathbf{192 \text{ bytes}}.
  • Total RAM: 10^9 \times 192 \text{ bytes} = \mathbf{192 \text{ Gigabytes}}.

Conclusion: ScaNN compressed the database by 93.7%, allowing the entire 1-Billion vector index to fit comfortably in the RAM of a single GCP high-memory instance, rather than requiring a massive distributed cluster.


Example 6: The Pareto frontier (Recall vs QPS)

Assume your Vertex AI index has 10,000 leaf nodes.

  • If leaf_nodes_to_search_percent = 10%, the system searches 1,000 leaves.
    • Math: 1,000 \text{ leaves} \times 500 \text{ vectors/leaf} = 500,000 \text{ vectors evaluated}.
    • Result: 98% Recall, 500 QPS.
  • If leaf_nodes_to_search_percent = 1%, the system searches 100 leaves.
    • Math: 100 \text{ leaves} \times 500 \text{ vectors/leaf} = 50,000 \text{ vectors evaluated}.
    • Result: 82% Recall, 4,500 QPS.

By tweaking a single parameter in the GCP SDK, you mathematically control the physics of your search infrastructure, balancing compute costs against retrieval perfection.

Let’s keep the conversation going! Share your thoughts, questions, and ideas in the comments.

Note: Should you have any concerns or queries about this post or my implementation, please feel free to connect with me on LinkedIn! Thanks!

9 Likes

Fantastic deep-dive @aniketagrawal! We are leveraging these exact ScaNN (Tree-AH) anisotropic quantization principles at CyberChurch Onchain to compress and index real-time 3D WebGL/XR spatial telemetry ([x, y, z] trajectory vectors) via GCP Cloud Run before generating on-chain state proofs. The sub-10ms latency and ~93% memory compression make real-time spatial anomaly detection at scale completely viable. Thanks for sharing the detailed math sandbox!

4 Likes

Excellent overview of ScaNN and graph routing mathematics! It’s rare to find an article that bridges abstract vector quantization loss functions with real-world GCP architecture so clearly. The dynamic lookup table (LUT) walkthrough was particularly helpful. Looking forward to more deep dives like this on the platform!

2 Likes

This is a great article with a lot of interesting examples and details. For some use cases the query is not the bottleneck, the indexing is. All the ANN and HNSW DBs are built for dumping data once and querying many times. But when you have a dynamic DB with many writes and deletes then the complexity of tree structure and indexing becomes an issue. There is an alternative way of approaching the vector search at scale without complex indexing using information-theoretic principles. You can find more about it in this article: [2601.11557] From HNSW to Information-Theoretic Binarization: Rethinking the Architecture of Scalable Vector Search
This engine has recently become available on GCP through this marketplace listing:
https://console.cloud.google.com/marketplace/product/edgeaiinnovations-public/moorcheh

2 Likes