Vector sizing calculator

The {vector-sizing-calculator} directive embeds an interactive calculator for estimating disk and off-heap memory for dense_vector fields. The docs site loads the React/EUI web component that registers the <vector-sizing-calculator> custom element.

:::{vector-sizing-calculator}
:::
		

No body content or options are required. Configuration happens in the widget UI.

This directive only produces useful output where the assembled documentation site includes the vector sizing bundle (see src/Elastic.Documentation.Site/Assets/web-components/VectorSizingCalculator/).

Warning

Draft for docs-content. This section is a staging copy of the kNN memory guidance the calculator implements. Merge it into deploy-manage/production-guidance/optimize-performance/approximate-knn-search.md (Ensure data nodes have enough memory) in a follow-up PR, then delete it from this syntax page.

Elasticsearch uses either the Hierarchical Navigable Small World (HNSW) algorithm or the Disk Better Binary Quantization (DiskBBQ) algorithm for approximate kNN search.

HNSW is a graph-based algorithm which only works efficiently when most vector data is held in memory. You should ensure that data nodes have at least enough RAM to hold the vector data and index structures.

DiskBBQ is a clustering algorithm which can scale efficiently often on less memory than HNSW. Where HNSW typically performs poorly without sufficient memory to fit the entire structure in RAM, DiskBBQ scales linearly when using less available memory than the total index size. You can start with enough RAM to hold the vector data and index structures but, in most cases, you should be able to reduce your RAM allocation and still maintain good performance. In testing, keeping all centroids resident plus as little as 5-10% of the posting lists (cluster vectors) in off-heap RAM is sufficient for reasonable performance for each set of queries that accesses largely overlapping clusters.

To check the size of the vector data, you can use the Analyze index disk usage API.

Tip

For float vectors with dim greater than or equal to 384, using a quantized index is highly recommended. Quantization can reduce off-heap RAM by 4×, 8×, or as much as 32×.

Note

Elasticsearch supports a maximum of 4,096 dimensions for dense_vector fields. Refer to the dense_vector mapping reference for supported parameters and limits.

Disk usage for a dense_vector field consists of three components: raw vector storage, quantized vector storage (if quantization is enabled), and index structure overhead.

The raw (unquantized) vectors are always stored on disk regardless of quantization settings. The size depends on the element_type:

element_type Bytes per dimension Disk per vector
float 4 num_dimensions × 4
bfloat16 2 num_dimensions × 2
byte 1 num_dimensions
bit 1/8 ⌈num_dimensions / 8⌉
\text{raw\_vector\_bytes} = \text{num\_vectors} \times \text{bytes\_per\_vector}

When quantization is enabled, Elasticsearch stores both the raw vectors and an additional set of quantized vectors. This increases total disk usage but reduces off-heap RAM requirements. Quantized vector storage only applies to float and bfloat16 element types.

quantization Additional bytes per vector
int8 num_dimensions + 16
int4 ⌈num_dimensions / 2⌉ + 16
bbq ⌈num_dimensions / 64⌉ × 8 + 14
\text{quantized\_disk} = \text{num\_vectors} \times \text{quantized\_bytes\_per\_vector}

The index structure overhead depends on the algorithm used:

The HNSW graph stores neighbor connections for each vector. The default value for m (connections per node) is 16.

\text{hnsw\_graph\_bytes} = \text{num\_vectors} \times 4 \times m

With the default m = 16:

\text{hnsw\_graph\_bytes} = \text{num\_vectors} \times 64

The flat (brute-force) index has no additional index structure on disk. Only the raw and quantized vectors are stored.

\text{flat\_index\_bytes} = 0

DiskBBQ stores cluster centroids and quantized vectors within clusters. The default value for vectors_per_cluster is 384.

First, compute the number of clusters:

\text{num\_clusters} = \left\lceil \frac{\text{num\_vectors}}{\text{vectors\_per\_cluster}} \right\rceil

Then compute the centroid and quantized vector storage. Centroids are 7-bit OSQ-quantized (1 byte per dimension) plus a 16-byte correction:

\text{centroid\_bytes} = \text{num\_clusters} \times (\text{num\_dimensions} + 16)

Cluster vectors use 1-bit codes when num_dimensions ≥ 384 (⌈num_dimensions / 8⌉ bytes) and 4-bit codes below that (⌈num_dimensions / 2⌉ bytes), each with a 16-byte correction. The × 2 is a conservative upper bound for SOAR overspill (a vector may be written to a second cluster):

\text{quantized\_vector\_bytes} = \text{num\_vectors} \times 2 \times (\text{cluster\_code\_bytes} + 16) \text{diskbbq\_total\_bytes} = \text{centroid\_bytes} + \text{quantized\_vector\_bytes}
\text{total\_disk} = \text{raw\_vector\_bytes} + \text{quantized\_disk} + \text{index\_structure\_bytes}

Disk and off-heap RAM are two different numbers, and they can differ by a lot. Disk is every structure persisted for the field; off-heap RAM is the working set that must stay in the operating system's filesystem cache for fast, stable query latency. Vector data is memory-mapped, so it lives in the OS page cache, separate from the Java heap.

Provision at least the off-heap RAM figure per copy, plus headroom. Once the working set no longer fits in cache, queries start reading from disk and latency climbs sharply. For quantized indexes the raw vectors stay on disk (read only for optional rescoring), so they count toward disk but not toward the required off-heap RAM.

What should stay in RAM, by index type:

  • flat: the raw vectors.
  • hnsw: the raw vectors and the graph.
  • int8_flat / int4_flat / bbq_flat: the quantized codes only.
  • int8_hnsw / int4_hnsw / bbq_hnsw: the quantized codes and the graph.
  • bbq_disk (DiskBBQ): all centroids plus a small fraction (5-10%) of the posting lists; the rest of the postings and the raw vectors stay on disk. This is why DiskBBQ can serve far more vectors per GiB of RAM.

The amount of vector data held in off-heap RAM depends on the element_type and quantization. When quantization is enabled, only the smaller quantized vectors need to be in RAM - the raw vectors are accessed from disk only during rescoring.

element_type quantization RAM per vector
float none num_dimensions × 4
float int8 num_dimensions + 16
float int4 ⌈num_dimensions / 2⌉ + 16
float bbq ⌈num_dimensions / 64⌉ × 8 + 14
bfloat16 none num_dimensions × 2
bfloat16 int8 num_dimensions + 16
bfloat16 int4 ⌈num_dimensions / 2⌉ + 16
bfloat16 bbq ⌈num_dimensions / 64⌉ × 8 + 14
byte none num_dimensions
bit none ⌈num_dimensions / 8⌉
\text{vector\_ram} = \text{num\_vectors} \times \text{ram\_per\_vector}

The HNSW graph must be fully loaded in memory for efficient search. The default value for m is 16.

\text{hnsw\_ram} = \text{num\_vectors} \times 4 \times m

Total off-heap RAM for HNSW:

\text{total\_ram} = \text{vector\_ram} + \text{hnsw\_ram}

The flat index has no graph structure. Only vector data needs to be in RAM.

\text{total\_ram} = \text{vector\_ram}

DiskBBQ keeps all centroids resident and needs only a fraction of the posting lists (cluster vectors) in off-heap RAM. In testing, all centroids plus 5-10% of the posting lists provides reasonable performance.

\text{diskbbq\_ram} \approx \text{centroid\_bytes} + (0.05 \text{ to } 0.10) \times \text{quantized\_vector\_bytes}
Tip

Start with all centroids plus ~5% of the posting lists in RAM and tune based on benchmark results. The required fraction depends on your query patterns - queries that access overlapping clusters benefit from caching more.

Each shard replica holds a full copy of the vector data and index structures. To estimate cluster-wide resource requirements, multiply the per-replica estimates by the total number of copies:

\text{total\_copies} = 1 \text{ (primary)} + \text{num\_replicas} \text{cluster\_disk} = \text{total\_disk\_per\_replica} \times \text{total\_copies} \text{cluster\_ram} = \text{total\_ram\_per\_replica} \times \text{total\_copies}
Note

The cluster-wide RAM is spread across data nodes that hold the shard replicas. Each data node only needs enough RAM for the replicas assigned to it.

These estimates are a planning baseline, not a guarantee. Real usage depends on your data, indexing settings, query patterns, merges, deletes, and rescoring options.

  • The raw vectors are always kept on disk, even for quantized indexes, because they are used for rescoring. Disk therefore barely changes across quantization types while required RAM drops sharply.
  • The HNSW graph size (num_vectors × 4 × m) is a planning heuristic; the stored graph is delta-encoded and its exact size varies with segment size.
  • The DiskBBQ off-heap RAM band (all centroids plus 5-10% of the posting lists) is benchmark-based; validate it against your own workload.
  • The × 2 on DiskBBQ cluster vectors is a conservative upper bound for SOAR overspill (a vector may be written to a second cluster); real usage is between 1× and 2×.
  • Sizes use binary units (1 GiB = 1,024 MiB).
  • Figures assume newly-indexed data on the current Elasticsearch codecs; upgraded segments may use different layouts.

The following table shows the RAM reduction factor for float vectors at common dimensions. All values assume HNSW with m = 16.

Dimensions No quantization int8 int4 bbq
384 1,536 B/vec 400 B/vec (3.8×) 208 B/vec (7.4×) 62 B/vec (24.8×)
768 3,072 B/vec 784 B/vec (3.9×) 400 B/vec (7.7×) 110 B/vec (27.9×)
1,024 4,096 B/vec 1,040 B/vec (3.9×) 528 B/vec (7.8×) 142 B/vec (28.8×)
1,536 6,144 B/vec 1,552 B/vec (4.0×) 784 B/vec (7.8×) 206 B/vec (29.8×)
Note

Values are vector data per vector (quantized code + correction). Add the constant HNSW graph overhead (~64 bytes per vector at m = 16) for total off-heap RAM; it applies equally to every HNSW variant, so it is excluded from the reduction factors.

Warning

Draft for a later docs-content PR. This copy used to render inside the vector sizing calculator ("How it is computed" and "Component breakdown (per replica)"). Merge it into the docs-content kNN page when the calculator ships on elastic.co/docs, then delete this subsection. Formulas above already cover the same math; keep the component-file table even if the prose is folded in.

Sizing a dense_vector field comes down to two figures, and they can differ by a lot:

  • Disk — every structure persisted for the field: the raw vectors, any quantized copies, and the search structure (HNSW graph or DiskBBQ clusters).
  • Off-heap RAM — the working set that must stay in the operating system's filesystem cache for fast, stable query latency. Vector data is memory-mapped, so it lives in the OS page cache, separate from the Java heap.

Provision at least the off-heap RAM figure per copy, plus headroom. Once the working set no longer fits in cache, queries start reading from disk and latency climbs sharply. For quantized indexes the raw vectors stay on disk (read only for optional rescoring), so they count toward disk but not toward the required RAM.

What must stay in RAM, per index type:

  • flat — the raw vectors.
  • hnsw — the raw vectors and the graph.
  • int8_flat / int4_flat / bbq_flat — the quantized codes only.
  • int8_hnsw / int4_hnsw / bbq_hnsw — the quantized codes and the graph.
  • bbq_disk (DiskBBQ) — all centroids plus a small fraction (5–10%) of the posting lists. The rest of the postings and the raw vectors stay on disk, and only clusters a query touches are paged in. This is why DiskBBQ can serve far more vectors per GiB of RAM.

Quantization keeps the raw vectors on disk but only needs the much smaller codes in memory — so disk barely changes across quantization types while required RAM drops sharply.

How each size is calculated

With V vectors, D dimensions, m graph connections per node (default 16), C DiskBBQ vectors per cluster (default 384), and f bytes per element (float 4, bfloat16 2, byte 1, bit 1/8):

HNSW and flat indexes — total on disk = raw vectors + quantized codes (if any) + graph (HNSW only):

  • Raw vectors (always kept): V × D × f (bit: V × ⌈D/8⌉).
  • Quantization: none — no extra codes; search uses the raw vectors.
  • Quantization: int8: V × (D + 16) — 1 byte per dimension plus a 16-byte correction.
  • Quantization: int4: V × (⌈D/2⌉ + 16) — half a byte per dimension plus the 16-byte correction.
  • Quantization: BBQ: V × (⌈D/64⌉×8 + 14) — 1 bit per dimension (padded up to a multiple of 64) plus a 14-byte correction.
  • HNSW graph (hnsw only): V × 4 × m — the planning estimate; the real graph is compressed and varies.

DiskBBQ (bbq_disk) — total on disk = raw vectors + centroids + clusters:

  • Raw vectors (always kept): V × D × f.
  • Centroids: ⌈V / C⌉ × (D + 16).
  • Clusters: V × 2 × (⌈D/8⌉ + 16) when D ≥ 384 (1-bit codes), otherwise V × 2 × (⌈D/2⌉ + 16) (4-bit). The ×2 covers vectors that spill into a second cluster.

The 14- or 16-byte "correction" is a small per-vector value the quantizer stores so it can rescore accurately.

Assumptions and limitations

  • These are estimates. Real usage depends on your data, indexing settings, query patterns, merges, deletes, and rescoring options.
  • Raw vectors are always kept, even for quantized indexes (they are needed for rescoring).
  • The HNSW graph size (V × 4 × m) is a planning heuristic; the stored graph is compressed and varies with segment size.
  • The DiskBBQ RAM band (all centroids + 5–10% of posting lists) is benchmark-based — validate it against your workload.
  • Sizes use binary units (1 GiB = 1,024 MiB).
  • Figures assume newly-indexed data on current Elasticsearch codecs.

Elasticsearch persists each structure as a Lucene file (also reported under off_heap.*_size_bytes). Off-heap RAM is whether that file must stay in the filesystem cache:

  • Yes — must stay resident (filesystem cache).
  • Partial — only touched parts are paged in.
  • No — lives on disk, read on demand.
Component File Formula Off-heap RAM Applies to What it is
Raw vectors .vec V × D × f (bit: V × ⌈D/8⌉) Yes if unquantized HNSW/flat; otherwise No All Full-precision vectors. Scanned during search when there is no quantization; otherwise kept on disk for optional rescoring.
int8 quantized vectors .veq V × (D + 16) Yes HNSW/flat + int8 1 byte/dim plus a 16-byte OSQ correction (3 floats + int component sum).
int4 quantized vectors .veq V × (⌈D/2⌉ + 16) Yes HNSW/flat + int4 0.5 byte/dim (nibble-packed) plus the same 16-byte OSQ correction.
BBQ quantized vectors .veb V × (⌈D/64⌉×8 + 14) Yes HNSW/flat + bbq 1 bit/dim (D padded up to a multiple of 64) plus a 14-byte correction (3 floats + short).
HNSW graph .vex V × 4 × m Yes hnsw Proximity graph, ~4 bytes per neighbour × m. Heuristic; the real .vex is varint delta-encoded and multi-level.
DiskBBQ centroids .cenivf ⌈V / C⌉ × (D + 16) Yes bbq_disk 7-bit OSQ centroids (1 byte/dim) plus a 16-byte correction. Lower bound: excludes the parent centroid layer and vector→centroid lookup table.
DiskBBQ clusters .clivf V × 2 × (⌈D/8⌉ + 16) when D ≥ 384; else V × 2 × (⌈D/2⌉ + 16) Partial bbq_disk 1-bit OSQ vectors when D ≥ 384, otherwise 4-bit, plus a 16-byte correction. The ×2 is a conservative SOAR-overspill upper bound. Only touched clusters are paged in.

The data nodes should also leave a buffer for other ways that RAM is needed. For example your index might also include text fields and numerics, which also benefit from using filesystem cache. It's recommended to run benchmarks with your specific dataset to ensure there's a sufficient amount of memory to give good search performance. You can find here and here some examples of datasets and configurations that we use for our nightly benchmarks.