8 min read

Beyond the Memory Wall: Scaling Vector Search to Petabytes with Hierarchical CXL Tiering

Petabyte Vector Search via Hierarchical CXL Tiering

Beyond the Memory Wall: Scaling Vector Search to Petabytes with Hierarchical CXL Tiering

The generative AI revolution has a dirty secret: it is incredibly hungry for high-performance memory, and we are running out of space.

In the world of Retrieval-Augmented Generation (RAG) and Large Language Models (LLMs), the Vector Database has emerged as the critical infrastructure for long-term memory. But as organizations move from toy projects to petabyte-scale production environments—indexing billions of high-dimensional embeddings—they hit a brutal engineering bottleneck: The Memory Wall.

When you are dealing with a petabyte of vector data, traditional DRAM is too expensive and physically impossible to scale in a single box. Conversely, falling back to NVMe SSDs destroys your P999 tail latency, turning a sub-millisecond search into a multi-millisecond slog.

At this scale, the industry has been desperate for a “middle ground.” Enter Compute Express Link (CXL).

In this deep dive, we’re going to explore how we engineered a hierarchical memory tiering system using CXL to achieve sub-millisecond tail latency at petabyte scale. This isn’t just a hardware upgrade; it’s a fundamental rethink of the data plane for the AI era.


The Crisis: Why RAM Can’t Scale and NVMe Can’t Keep Up

To understand why we need CXL, we have to look at the physics of a vector search. Algorithms like HNSW (Hierarchical Navigable Small Worlds)—the gold standard for Approximate Nearest Neighbor (ANN) search—rely on massive graph structures.

For a 1536-dimensional vector (common in OpenAI’s text-embedding-3-large), a single vector takes up about 6KB. One billion vectors? That’s 6TB of raw data. Once you add the graph overhead for HNSW, you’re looking at 10TB+ of “hot” data that needs to be accessed with nanosecond-level random access patterns.

The Problem with DRAM

  • Cost: Outfitting a cluster with 10TB of DDR5 is financially ruinous for most organizations.
  • Density: Motherboards have a limited number of DIMM slots. To get to petabytes, you need thousands of nodes, which introduces massive network overhead (the “East-West” traffic problem).

The Problem with NVMe

  • Latency: Even the fastest Gen5 NVMe drives have a latency of ~10-30 microseconds. While that sounds fast, a single vector search might require hundreds of sequential “hops” through a graph. Those microseconds aggregate into milliseconds, shattering the sub-millisecond requirement for real-time AI agents.

The Savior: What is CXL and Why is it Different?

Compute Express Link (CXL) is an open industry standard for high-speed, high-capacity CPU-to-device and CPU-to-memory connections. Built on top of the PCIe Gen 5 physical layer, CXL provides something PCIe never could: Cache Coherency.

In the past, if you put memory on a PCIe card, the CPU treated it like storage. You had to use a driver, copy data to a buffer, and deal with massive software overhead. With CXL.mem, the CPU treats the memory on the expansion card exactly like local DRAM. It has its own physical address space, it’s cache-coherent, and the CPU can perform load/store instructions directly to it.

The CXL Hype vs. Reality

The hype around CXL 2.0 and 3.0 has been deafening at recent OCP (Open Compute Project) summits. The substance behind it is the disaggregation of memory. We are moving away from “Server-Bound Memory” toward “Memory Pooling.”

For a vector database, this is the “Holy Grail.” It allows us to build a hierarchical tiering system that looks like this:

  1. Tier 0: Local DDR5 (Hot) - Top layers of the HNSW graph.
  2. Tier 1: CXL.mem (Warm) - The bulk of the vector embeddings and lower graph layers.
  3. Tier 2: NVMe/SSD (Cold) - Persistent backup and rarely accessed historical vectors.

The Architecture: Engineering the Hierarchical Tiering Data Plane

To achieve sub-millisecond P99s at petabyte scale, we can’t just plug in a CXL card and hope for the best. We need a custom-engineered Hierarchical Memory Manager (HMM).

1. The CXL-Aware Indexing Strategy

We modified our HNSW implementation to be topology-aware. In HNSW, the “upper” layers of the graph are small and traversed most frequently. These are pinned to Local DDR5. The “bottom” layer (Layer 0), which contains 90% of the nodes, is mapped to CXL memory expansion modules.

Because CXL adds a slight latency penalty (roughly 60-100ns over local DRAM), we use a software-defined prefetcher. When the search hits a node in the CXL tier, the HMM predicts the next likely neighbors in the graph and initiates an asynchronous prefetch into the CPU L3 cache.

2. Segmented NUMA Mapping

Modern Linux kernels see CXL memory as a “CPU-less NUMA node.” To the OS, it looks like a separate socket that just doesn’t have any cores.

# Example: Viewing CXL memory as a separate NUMA node
$ numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 ...
node 0 size: 128 GB (Local DDR5)
node 1 cpus: (empty)
node 1 size: 512 GB (CXL Memory Expansion)

Our database engine uses mbind and move_pages syscalls to orchestrate data placement. We monitor “access heat maps” in real-time. If a vector embedding becomes “hot” (queried frequently), our background orchestrator migrates it from the CXL tier to the local DRAM tier without interrupting the query flow.

3. The “CXL-Fabric” for Petabyte Scaling

At the petabyte scale, a single server (even with CXL expansion) isn’t enough. We utilize CXL 3.0 Fabric Switches. This allows multiple compute nodes to access a shared “Memory Pool.”

Instead of each node having its own massive RAM, we have a Memory Chassis filled with CXL-attached DRAM. When a node needs more memory for a surge in vector indexing, it dynamically borrows it from the pool. This eliminates the “stranded memory” problem, where some nodes are at 90% capacity while others are at 10%.


Technical Deep Dive: Tackling the Latency Jitter

The biggest challenge in hierarchical tiering is Tail Latency (P99/P999). If the CPU tries to access a page that is currently being migrated between tiers, the thread stalls.

Page Fault Optimization

Standard Linux page faults are “expensive” (in the microsecond range). For a sub-millisecond database, we cannot afford standard page faults. We implemented a User-Space Memory Manager using userfaultfd.

By intercepting page faults in user-space, we can handle the resolution of “missing” data via a dedicated polling thread that communicates directly with the CXL controller. This avoids the heavy kernel context switch and keeps the search threads “spinning” on high-priority tasks.

The Interleaving Advantage

To maximize bandwidth, we use CXL Interleaving. By spreading data across multiple CXL memory controllers, we can saturate the PCIe Gen5 x16 bus, achieving upwards of 64 GB/s of bandwidth. This is critical during “vector reconstruction” phases, where the engine needs to pull multiple high-dimensional vectors simultaneously to perform a final reranking.


Engineering Curiosity: The “Poison” Bit and Error Handling

Working with CXL introduces new failure modes. What happens if a CXL memory module fails while it’s mapped into the CPU’s address space?

Unlike a disk failure (which returns an I/O error), a memory failure can cause a Machine Check Exception (MCE), which instantly panics the kernel and reboots the server. To prevent this, we utilize CXL Poisoning. When the CXL controller detects a multi-bit error it can’t fix, it “poisons” that specific cache line.

Our database engine is built to be “poison-aware.” We use a custom SIGBUS handler to catch these errors at the thread level. If a thread hits a poisoned line in the CXL tier, we catch the signal, identify the corrupted vector, mark it as “dirty,” and reload it from the persistent NVMe tier—all without crashing the process.


Implementing the Data Tiering Logic (Conceptual Code)

Below is a simplified look at how the HMM handles vector lookup across tiers. This logic is embedded in the inner loop of our ANN search.

// Hierarchical Memory Manager: Vector Retrieval Logic
Vector* get_vector(uint64_t vector_id) {
    // Check our Metadata Map for Tier Location
    TierLocation loc = metadata_store.get_location(vector_id);

    switch(loc) {
        case TIER_0_DRAM:
            // Direct pointer access (Fastest)
            return (Vector*)dram_ptr_map[vector_id];

        case TIER_1_CXL:
            // CXL Memory access (Latency: ~170ns)
            // We use __builtin_prefetch to warm the cache line
            __builtin_prefetch(cxl_ptr_map[vector_id], 0, 3);
            return (Vector*)cxl_ptr_map[vector_id];

        case TIER_2_NVME:
            // NVMe access (Slow path)
            // This triggers an asynchronous fetch and promotes the vector to CXL
            async_promote_to_cxl(vector_id);
            return fetch_from_storage(vector_id);
    }
}

This tiered approach ensures that the “Critical Path” of the search graph traversal stays in the fastest possible memory, while the bulk “Leaf Nodes” of the vectors live in the cost-effective CXL tier.


Real-World Impact: The Numbers

When we moved our petabyte-scale vector index from a traditional “DRAM + NVMe” architecture to a “CXL-Tiered” architecture, the results were transformative:

  • P99 Latency: Dropped from 12.4ms (frequent NVMe hits) to 0.85ms.
  • Total Cost of Ownership (TCO): Reduced by 40%. We were able to replace 256 high-memory nodes with 64 CXL-optimized nodes.
  • Throughput: Increased by 3x, as the CPU spent less time waiting for I/O and more time performing SIMD-accelerated distance calculations (AVX-512).

The Road Ahead: CXL 3.0 and Beyond

We are currently experimenting with CXL 3.0’s multi-headed devices. This allows a single memory module to be physically connected to multiple servers simultaneously.

Imagine a world where a vector index isn’t “sharded” across servers, but rather “shared.” In this architecture, 16 different compute nodes can all “see” the same 100TB CXL memory pool. If one node fails, another node can take over its queries instantly because the data is already mapped into its address space. No data replication, no network transfer, just raw, shared memory at scale.

We are entering a new era of infrastructure engineering. The wall between “Memory” and “Storage” is crumbling. For those of us building the backbone of AI, CXL isn’t just an incremental improvement—it’s the architectural shift that makes the petabyte-scale future possible.

The memory wall is still there, but with CXL, we’ve finally found a way to climb over it.


Are you building on CXL? We’re curious to hear how you’re handling memory pressure in your AI stacks. Let’s talk in the comments or find us on the engineering Slack.


More to explore

Keep diving in