Embedding Dimensions: 384 vs 768 vs 1024 vs 1536 vs 3072 Explained

TL;DR — Embedding dimensions: 384 for prototyping, 768-1024 as production default, 1536 for OpenAI, 3072 for max quality. PragmaticByHarsh: "Start with 768-1024 dimensions. Modern models support Matryoshka truncation — cut dimensions in half with minimal quality loss. Quality doesn't scale linearly. Voyage-4-large at 1024-dim scores 72.3% MTEB vs OpenAI 3-large at 3072-dim 64.6%." CodeSOTA: "Default to 768 — best quality/cost ratio. Prototype at 384, deploy at 768+. Past 1024, gains are marginal. At 100M docs: 384d = 150GB vs 3072d = 1.2TB — 8x cost multiplier." AITLDR: "Under 1M vectors, 1536 is safe. At tens of millions, 768-1024 is practical. MTEB: 384d ~56, 768d ~63, 1024d ~64, 1536d ~64.6, 3072d ~65. Gains compress quickly." TypeGraph: "halfvec beats vector in pgvector — cuts storage in half while preserving quality. 1024d halfvec: nDCG 0.658, recall 0.92. Prefer halfvec unless benchmark proves float32 is worth it." Decompressed: "Start with text-embedding-3-small. MRL: cut 1536 to 512 dims, lose 2-4 MRR points, save two-thirds storage." Learn more with embeddings, embedding models, cosine similarity, and pgvector tutorial.

CodeSOTA explains the fundamentals: "Embedding dimensions like 384, 768, and 1024 look arbitrary — but they are the product of transformer architecture constraints, GPU hardware alignment, and information-theoretic bounds. Doubling the embedding dimension doubles memory, roughly doubles search latency for brute-force retrieval — but the quality gains diminish sharply."

PragmaticByHarsh provides the practical guidance: "Start with 768-1024 dimensions. This range balances quality, cost, and speed for most production systems. Modern embedding models support dimension reduction via Matryoshka learning. You can often cut dimensions in half with minimal quality loss, test this before defaulting to maximum dimensions."

Embedding Dimensions Architecture

flowchart TD subgraph Models["Embedding Models by Dimension"] M384["384 dims
all-MiniLM-L6-v2
MTEB: ~56
1.5 KB/vector"] M768["768 dims
nomic-embed-text
MTEB: ~63
3 KB/vector"] M1024["1024 dims
BGE-M3 / Voyage-4
MTEB: ~68-72
4 KB/vector"] M1536["1536 dims
OpenAI 3-small
MTEB: ~62
6 KB/vector"] M3072["3072 dims
OpenAI 3-large
MTEB: ~65
12 KB/vector"] end subgraph MRL["Matryoshka Truncation"] Full["Full: 1536 dims
100% quality"] Half["Truncate: 768 dims
99.2% quality"] Quarter["Truncate: 384 dims
98.1% quality"] Eighth["Truncate: 256 dims
96.5% quality"] end subgraph Storage["Storage Impact at 10M Vectors"] S384["384d: 15 GB
$1.50-3/mo"] S768["768d: 30 GB
$3-6/mo"] S1024["1024d: 40 GB
$4-8/mo"] S1536["1536d: 60 GB
$6-12/mo"] S3072["3072d: 120 GB
$12-24/mo"] end subgraph Decision["Selection Guide"] D1["Prototype → 384 dims"] D2["Production default → 768-1024 dims"] D3["High precision → 1536 dims"] D4["Max quality → 3072 dims (test first)"] D5["Storage constrained → halfvec + MRL"] end M384 --> D1 M768 --> D2 M1024 --> D2 M1536 --> D3 M3072 --> D4 Full --> Half --> Quarter --> Eighth Half --> D5 S384 --> Storage S768 --> Storage S1024 --> Storage S1536 --> Storage S3072 --> Storage

Dimension Comparison Table

Dimension Memory/Vector MTEB Avg Quality/KB Search Latency Best For
384 1.5 KB ~56 37.3 <10ms (fastest) Prototyping, edge, cost-sensitive
768 3 KB ~63 21.0 10-30ms (fast) Production default, best quality/cost
1024 4 KB ~64-72 16.0 30-50ms (moderate) RAG, retrieval-optimized models
1536 6 KB ~62-64.6 10.8 50-100ms (slower) OpenAI 3-small, general purpose
3072 12 KB ~65 5.4 100-500ms (slowest) Max quality, dense technical text

Storage Cost at Scale

Documents 384d 768d 1024d 1536d 3072d
10K 15 MB 30 MB 40 MB 60 MB 120 MB
100K 150 MB 300 MB 400 MB 600 MB 1.2 GB
1M 1.5 GB 3 GB 4 GB 6 GB 12 GB
10M 15 GB 30 GB 40 GB 60 GB 120 GB
100M 150 GB 300 GB 400 GB 600 GB 1.2 TB

MRL Truncation Quality Retention

Truncated To Quality Retained MTEB Delta Use Case
768 (from 1536) 100% 0 Full dimension
512 99.2% -0.5 Minimal loss
384 98.1% -1.2 Great tradeoff
256 96.5% -2.3 Prototyping
128 92.8% -4.8 Noticeable degradation
64 85.4% -9.7 Topic-level only

Implementation

from dataclasses import dataclass
from typing import Optional
from enum import Enum

class DimensionPreset(Enum):
    PROTOTYPE = 384
    PRODUCTION = 768
    RAG_OPTIMIZED = 1024
    OPENAI_SMALL = 1536
    MAX_QUALITY = 3072

@dataclass
class EmbeddingDimensionSelector:
    """Select and optimize embedding dimensions for production RAG."""

    def recommend(self, vector_count: int,
                  precision_required: str = "standard",
                  storage_budget_gb: float = None,
                  latency_budget_ms: float = None,
                  use_matryoshka: bool = True,
                  use_halfvec: bool = True) -> dict:
        """Recommend embedding dimensions based on constraints."""

        if vector_count < 100_000 and precision_required == "low":
            return self._result(384, "Prototyping: 384 dims, "
                              "fastest, lowest storage")

        if storage_budget_gb and vector_count > 0:
            max_dims_float32 = int(
                (storage_budget_gb * 1e9) /
                (vector_count * 4))
            max_dims_halfvec = int(
                (storage_budget_gb * 1e9) /
                (vector_count * 2))

            if use_halfvec and max_dims_halfvec >= 1024:
                return self._result(1024, 
                    "1024 dims with halfvec: fits budget, "
                    "best quality/cost",
                    use_halfvec=True)
            elif max_dims_float32 >= 768:
                return self._result(768,
                    "768 dims: fits storage budget, "
                    "production default")
            elif max_dims_float32 >= 384:
                return self._result(384,
                    "384 dims: fits storage budget, "
                    "prototyping quality")

        if latency_budget_ms and latency_budget_ms < 30:
            return self._result(384,
                "384 dims: meets latency budget "
                "(<10ms query)")

        if precision_required == "high":
            if use_matryoshka:
                return self._result(1024,
                    "1024 dims with MRL: high precision, "
                    "can truncate if needed",
                    use_matryoshka=True)
            return self._result(1536,
                "1536 dims: high precision, "
                "OpenAI 3-small default")

        if precision_required == "maximum":
            return self._result(3072,
                "3072 dims: maximum quality, "
                "test if improvement justifies cost")

        return self._result(768,
            "768 dims: production default, "
            "best quality/cost ratio")

    def get_storage_estimate(self, dimensions: int,
                             vector_count: int,
                             use_halfvec: bool = False) -> dict:
        """Estimate storage requirements."""
        bytes_per_float = 2 if use_halfvec else 4
        raw_bytes = dimensions * bytes_per_float * vector_count
        hnsw_overhead = raw_bytes * 2.5  # HNSW adds ~2.5x

        return {
            "dimensions": dimensions,
            "vector_count": vector_count,
            "storage_type": "halfvec" if use_halfvec else "vector",
            "bytes_per_vector": dimensions * bytes_per_float,
            "raw_storage_gb": raw_bytes / 1e9,
            "with_hnsw_gb": hnsw_overhead / 1e9,
            "monthly_cost": (
                hnsw_overhead / 1e9 * 0.15
            ),  # $0.10-0.20/GB/month
        }

    def get_mrl_truncation_guide(self,
                                 full_dims: int = 1536) -> dict:
        """Matryoshka Representation Learning truncation guide."""
        truncations = [
            (768, 100.0, 0, "Full dimension (if 768+)"),
            (512, 99.2, -0.5, "Minimal loss"),
            (384, 98.1, -1.2, "Great tradeoff"),
            (256, 96.5, -2.3, "Good for prototyping"),
            (128, 92.8, -4.8, "Noticeable degradation"),
            (64, 85.4, -9.7, "Topic-level only"),
        ]

        return {
            "full_dimensions": full_dims,
            "supported_models": [
                "OpenAI text-embedding-3-small (1536)",
                "OpenAI text-embedding-3-large (3072)",
                "nomic-embed-text-v1.5 (768)",
                "Cohere embed-v4 (1024)",
                "Voyage-4 (1024)",
                "gemini-embedding-001 (3072)",
            ],
            "truncation_options": [
                {
                    "dimensions": t[0],
                    "quality_retained": t[1],
                    "mteb_delta": t[2],
                    "note": t[3],
                    "storage_savings": (
                        1 - t[0] / full_dims
                    ),
                }
                for t in truncations
                if t[0] <= full_dims
            ],
            "notes": (
                "MRL allows truncation without retraining. "
                "Request fewer dimensions from same model. "
                "text-embedding-3-large at 256 dims "
                "outperforms ada-002 at 1536 dims. "
                "6x reduction with better quality."
            ),
        }

    def get_halfvec_comparison(self,
                               dimensions: int = 1024,
                               vector_count: int = 1_000_000) -> dict:
        """Compare vector vs halfvec storage and quality."""
        float32_bytes = dimensions * 4 * vector_count
        halfvec_bytes = dimensions * 2 * vector_count

        return {
            "dimensions": dimensions,
            "vector_count": vector_count,
            "vector_float32": {
                "bytes_per_vector": dimensions * 4,
                "total_storage_gb": float32_bytes / 1e9,
                "precision": "32-bit float",
            },
            "halfvec": {
                "bytes_per_vector": dimensions * 2,
                "total_storage_gb": halfvec_bytes / 1e9,
                "precision": "16-bit float",
                "storage_reduction": "50%",
            },
            "quality_comparison": {
                "vector_ndcg": 0.6550,
                "halfvec_ndcg": 0.6580,
                "vector_recall": 0.9100,
                "halfvec_recall": 0.9200,
                "verdict": "halfvec matches or slightly "
                           "exceeds vector quality",
            },
            "recommendation": (
                "Prefer halfvec over vector unless "
                "benchmark proves float32 is worth "
                "the extra storage. halfvec cuts "
                "storage in half while preserving "
                "retrieval quality."
            ),
        }

    def _result(self, dims: int, reason: str,
                use_halfvec: bool = False,
                use_matryoshka: bool = False) -> dict:
        return {
            "recommended_dimensions": dims,
            "reason": reason,
            "use_halfvec": use_halfvec,
            "use_matryoshka": use_matryoshka,
            "memory_per_vector_kb": round(dims * 4 / 1024, 1),
            "latency_estimate_ms": self._estimate_latency(dims),
        }

    def _estimate_latency(self, dims: int) -> str:
        if dims <= 256:
            return "<5ms"
        elif dims <= 384:
            return "<10ms"
        elif dims <= 512:
            return "10-20ms"
        elif dims <= 768:
            return "10-30ms"
        elif dims <= 1024:
            return "30-50ms"
        elif dims <= 1536:
            return "50-100ms"
        else:
            return "100-500ms"

Embedding Dimensions Checklist

  • [ ] Embedding dimensions are the number of values used to represent a chunk, document, or query
  • [ ] 384 dimensions: 1.5 KB/vector, MTEB ~56, fastest, for prototyping and edge
  • [ ] 768 dimensions: 3 KB/vector, MTEB ~63, fast, production default, best quality/cost
  • [ ] 1024 dimensions: 4 KB/vector, MTEB ~64-72, moderate, RAG-optimized models (BGE-M3, Voyage-4)
  • [ ] 1536 dimensions: 6 KB/vector, MTEB ~62-64.6, slower, OpenAI 3-small default
  • [ ] 3072 dimensions: 12 KB/vector, MTEB ~65, slowest, max quality, dense technical text
  • [ ] Start with 768-1024 dimensions for most production systems
  • [ ] Prototype at 384, deploy at 768+ — dimension is a tuning knob, not a fixed choice
  • [ ] Past 1024, gains are marginal — spend budget on better data instead
  • [ ] Quality doesn't scale linearly with dimensions — training matters more than size
  • [ ] Voyage-4-large at 1024-dim scores 72.3% MTEB vs OpenAI 3-large at 3072-dim 64.6%
  • [ ] A well-trained 1024-dim model can beat a poorly-trained 3072-dim model
  • [ ] Storage scales linearly: 10M vectors at 384d = 15GB, at 3072d = 120GB (8x cost)
  • [ ] At 100M docs: 384d = 150GB vs 3072d = 1.2TB — 8x infrastructure cost multiplier
  • [ ] Vector database storage costs $0.10-0.20/GB/month
  • [ ] HNSW index adds ~2.5x on top of raw vector storage
  • [ ] Latency by dimensions: 256 (<5ms), 384 (<10ms), 512 (10-20ms), 768 (10-30ms), 1024 (30-50ms), 1536 (50-100ms), 3072 (100-500ms)
  • [ ] Matryoshka Representation Learning (MRL) allows truncation without retraining
  • [ ] MRL supported by: OpenAI 3-series, Cohere v4, Voyage v4, gemini-embedding-001, nomic-embed-text
  • [ ] MRL truncation: 768 (100% quality), 512 (99.2%), 384 (98.1%), 256 (96.5%), 128 (92.8%)
  • [ ] text-embedding-3-large at 256 dims outperforms ada-002 at 1536 dims — 6x reduction with better quality
  • [ ] Cut text-embedding-3-small from 1536 to 512: lose 2-4 MRR points, save two-thirds storage
  • [ ] Use MRL truncation when storage or ANN speed is a constraint
  • [ ] Measure accuracy penalty before assuming truncation is acceptable
  • [ ] pgvector halfvec cuts storage roughly in half while preserving retrieval quality
  • [ ] 1024d vector: ~4KB, 1024d halfvec: ~2KB — 50% storage reduction
  • [ ] halfvec reduction also matters for HNSW index size (less data to keep hot)
  • [ ] Test results: 1024d halfvec nDCG 0.658, recall 0.92 — slightly exceeds float32 vector
  • [ ] Prefer pgvector halfvec over vector unless benchmark proves float32 is worth extra storage
  • [ ] Use halfvec when: storage cost matters, HNSW memory constrained, >1M vectors
  • [ ] Quality/KB efficiency: 384d (37.3), 768d (21.0), 1024d (16.0), 1536d (10.8), 3072d (5.4)
  • [ ] 768 is popular because you get most quality at a fraction of cost
  • [ ] Under 1M vectors: 1536 dimensions is a safe default
  • [ ] At tens of millions of vectors: 768-1024 dimensions more practical
  • [ ] For very high precision (multilingual, dense technical): 3072 may justify cost
  • [ ] Always benchmark recall on a sample of your own data
  • [ ] Start with text-embedding-3-small — fast, cheap, reliable for general retrieval
  • [ ] Test text-embedding-3-large if precision matters and cost is secondary
  • [ ] If improvement is 3-4 MRR points, 6.5x cost increase might be worth it
  • [ ] If improvement is 1 MRR point, it isn't worth the cost
  • [ ] Dimensions are determined by transformer architecture (heads × head_dim)
  • [ ] 384 = 6 heads × 64, 768 = 12 heads × 64, 1024 = 16 heads × 64, 1536 = 24 heads × 64
  • [ ] Read embeddings for fundamentals
  • [ ] Read embedding models for model selection
  • [ ] Read cosine similarity for distance metrics
  • [ ] Read pgvector tutorial for setup with halfvec
  • [ ] Read what is RAG for RAG architecture
  • [ ] Read vector databases for storage
  • [ ] Test: 768 dims achieves ~63 MTEB with 3KB/vector
  • [ ] Test: MRL truncation from 1536 to 384 retains 98.1% quality
  • [ ] Test: halfvec matches float32 quality at half storage
  • [ ] Test: storage estimate matches actual index size
  • [ ] Test: recommendation logic selects correct dimensions per constraints
  • [ ] Document dimension choice, MRL strategy, halfvec usage, and benchmark results

FAQ

What embedding dimensions should you choose for production RAG?

Start with 768-1024 dimensions for most production systems — this range balances quality, cost, and speed. PragmaticByHarsh: "Start with 768-1024 dimensions. This range balances quality, cost, and speed for most production systems. Modern embedding models support dimension reduction via Matryoshka learning. You can often cut dimensions in half with minimal quality loss." Decompressed: "Start with text-embedding-3-small. It is fast, cheap, and reliable on general English-language retrieval. For most teams, most of the time, it is the right answer." CodeSOTA: "Default to 768 — best quality/cost ratio for most applications. Use Matryoshka models — train once, truncate to any dimension at inference. Prototype at 384, deploy at 768+. Past 1024, gains are marginal — spend your budget on better data instead." AITLDR: "Under 1 million vectors, 1536 dimensions is a safe default. At tens of millions, 768-1024 is more practical. For very high precision, 3072 may justify the cost. Always benchmark recall on your own data."

How do embedding dimensions affect storage cost and latency?

Storage and latency scale linearly with dimensions. Going from 768 to 3072 dimensions quadruples storage and significantly increases search latency. AITLDR: "Every vector in your database scales with dimensions. Going from 768 to 3072 quadruples raw storage. At 10 million vectors: 230 MB at 384 dims vs 3.7 GB at 3072 dims in float32. Cloud vector database pricing follows storage." CodeSOTA: "At 100M documents, difference between 384d and 3072d is 150 GB vs 1.2 TB — an 8x cost multiplier. Doubling dimensions roughly doubles search latency for brute-force retrieval, but quality gains diminish sharply." PragmaticByHarsh: "Vector database storage typically costs $0.10-0.20/GB/month. At 10M documents with 1536-dim: $6-12/month. Cut to 768-dim: $3-6/month." Latency by dimensions: 256 (<5ms), 384 (<10ms), 512 (10-20ms), 768 (10-30ms), 1024 (30-50ms), 1536 (50-100ms), 3072 (100-500ms).

What is Matryoshka Representation Learning (MRL) and how does it help?

MRL allows you to truncate embedding dimensions without retraining — you can request fewer dimensions from the same model with minimal quality loss. PragmaticByHarsh: "Modern embedding models (OpenAI 3-series, Cohere v4, Voyage v4, gemini-embedding-001) support Matryoshka Representation Learning. You can reduce dimensions without retraining. text-embedding-3-large at 256 dimensions outperforms ada-002 at 1536 dimensions on MTEB. That is a 6x reduction in size with better quality." Decompressed: "Models trained with MRL produce vectors you can truncate to a smaller size without proportional quality loss. Both text-embedding-3-small and text-embedding-3-large support MRL: cut text-embedding-3-small from 1536 to 512 dimensions and lose roughly 2-4 MRR points while saving two-thirds of storage." CodeSOTA: "Use Matryoshka models — train once, truncate to any dimension at inference. Prototype at 384, deploy at 768+ — dimension is a tuning knob, not a fixed choice." MRL truncation quality: 768 (100%), 512 (99.2%), 384 (98.1%), 256 (96.5%), 128 (92.8%).

Should you use pgvector halfvec instead of vector for embeddings?

Yes, halfvec cuts storage roughly in half while preserving retrieval quality, making it the preferred choice for most pgvector deployments. TypeGraph: "halfvec beats vector when using pgvector on Postgres because it cuts raw vector storage roughly in half while preserving retrieval quality. A 1024-dimensional vector is roughly 4KB. A 1024-dimensional halfvec is roughly 2KB. That reduction also matters for HNSW index size. Prefer pgvector halfvec over vector unless your benchmark proves float32 is worth the extra storage." Test results: 1024 dims vector nDCG@10: 0.6550, recall@10: 0.9100. 1024 dims halfvec nDCG@10: 0.6580, recall@10: 0.9200 — halfvec actually scored slightly higher. Use halfvec when: (1) Storage cost matters. (2) HNSW index memory is a constraint. (3) You have >1M vectors. (4) Your benchmark shows no significant quality loss.

Do higher embedding dimensions always mean better quality?

No — quality doesn't scale linearly with dimensions. A well-trained 1024-dim model can beat a poorly-trained 3072-dim model. Training matters more than size. PragmaticByHarsh: "Quality does not scale linearly with dimensions. A well-trained 1024-dim model can beat a poorly-trained 3072-dim model. Training matters more than size. Voyage-4-large at 1024-dim scores 72.3% MTEB. OpenAI text-embedding-3-large at 3072-dim scores 64.6% MTEB. The 1024-dim model wins because it is trained specifically for retrieval." AITLDR: "Larger default dimension does not automatically mean better MTEB score. Voyage AI voyage-3 achieves competitive quality at 1024 dimensions, costing 3-4x less storage than OpenAI 3072-dimensional model. The quality of training data and training objective matter at least as much as raw dimension count." CodeSOTA: "Past 1024, gains are marginal — spend your budget on better data instead." MTEB scores by dimension: 384 (~56), 768 (~63), 1024 (~64), 1536 (~64.6), 3072 (~65). Quality/KB efficiency: 384d (37.3), 768d (21.0), 1024d (16.0), 1536d (10.8), 3072d (5.4).


Want a self-hosted AI company brain that does all of this out of the box?
Book a demo →