Applies to: SharpCoreDB.VectorSearch 1.2.0+ | .NET 10 | C# 14
| Dataset Size | Recommended Index | Recall | Latency |
|---|---|---|---|
| < 10K vectors | FLAT |
100% (exact) | < 5ms |
| 10K–1M vectors | HNSW (default) |
> 95% | < 2ms |
| > 1M vectors | HNSW (tuned) |
> 90% | < 10ms |
-- Small dataset: exact search (no index needed, or FLAT)
CREATE VECTOR INDEX idx ON docs(embedding) USING FLAT
-- Large dataset: approximate search
CREATE VECTOR INDEX idx ON docs(embedding) USING HNSW| Parameter | Default | Range | Effect |
|---|---|---|---|
M |
16 | 4–64 | Connections per node. Higher = better recall, more memory |
EfConstruction |
200 | 50–800 | Build-time search width. Higher = better index quality, slower builds |
EfSearch |
50 | 10–500 | Query-time search width. Higher = better recall, slower queries |
// High recall (> 99%): slower queries, more memory
services.AddVectorSupport(opt => {
opt.DefaultM = 32;
opt.DefaultEfConstruction = 400;
opt.DefaultEfSearch = 200;
});
// Low memory: faster queries, slightly lower recall
services.AddVectorSupport(opt => {
opt.DefaultM = 8;
opt.DefaultEfConstruction = 100;
opt.DefaultEfSearch = 30;
});
// Balanced (default)
services.AddVectorSupport(); // M=16, efConstruction=200, efSearch=50- Start with defaults — 95%+ recall for most datasets
- Measure recall — compare HNSW results with exact (FLAT) search
- Increase
EfSearchfirst if recall is too low (cheapest knob) - Increase
Mif recall plateaus — requires index rebuild - Increase
EfConstructionfor better index quality — requires rebuild
| Function | Best For | SQL Name |
|---|---|---|
| Cosine | Normalized embeddings (OpenAI, Cohere) | vec_distance_cosine |
| Euclidean (L2) | Raw/unnormalized vectors | vec_distance_l2 |
| Dot Product | Maximum inner product search | vec_distance_dot |
Tip: If your embeddings are already L2-normalized (most LLM providers), cosine and dot product give identical rankings. Use cosine for clarity.
| Method | Memory Reduction | Recall Impact | Use Case |
|---|---|---|---|
| None (float32) | 1× | Perfect | < 100K vectors |
| Scalar (uint8) | 4× | ~1-3% loss | 100K–1M vectors |
| Binary (1-bit) | 32× | ~5-10% loss | > 1M vectors, first-pass filtering |
// Embedded/mobile — 50 MB limit
services.AddVectorSupport(opt => {
opt.MaxMemoryMB = 50;
opt.LazyIndexLoading = true;
opt.EvictIndexOnMemoryPressure = true;
});
// Server — unlimited
services.AddVectorSupport(opt => {
opt.MaxMemoryMB = 0;
opt.LazyIndexLoading = false;
});Per vector in HNSW index:
- Vector data:
dimensions × 4 bytes(float32) - Graph links:
M × 2 × 8 bytesper layer (bidirectional long IDs) - Overhead: ~64 bytes per node
Example: 100K vectors × 1536 dims × M=16:
- Vector data: 100K × 1536 × 4 = 585 MB
- Graph links: 100K × 16 × 2 × 8 ≈ 25 MB
- Total: ~610 MB
SharpCoreDB.VectorSearch uses System.Runtime.Intrinsics for multi-tier SIMD:
| Tier | Width | Floats/op | Available On |
|---|---|---|---|
| AVX-512 | 512-bit | 16 | Intel Ice Lake+, AMD Zen 4+ |
| AVX2 | 256-bit | 8 | Intel Haswell+ (2013), AMD Zen+ |
| SSE | 128-bit | 4 | All x64 CPUs |
| Scalar | 32-bit | 1 | Universal fallback |
FMA (Fused Multiply-Add): Automatically used when available — better throughput and precision for dot product / cosine.
using System.Runtime.Intrinsics.X86;
Console.WriteLine($"AVX-512: {Avx512F.IsSupported}");
Console.WriteLine($"AVX2: {Avx2.IsSupported}");
Console.WriteLine($"FMA: {Fma.IsSupported}");
Console.WriteLine($"SSE: {Sse.IsSupported}");The query planner detects this pattern and routes to the vector index:
SELECT id, title, vec_distance_cosine(embedding, @query) AS distance
FROM documents
ORDER BY distance
LIMIT 10Without index: Full table scan → compute distance for ALL rows → sort → take 10 With HNSW index: Index search → return top 10 pre-sorted → skip scan and sort
EXPLAIN SELECT id, vec_distance_cosine(embedding, @query) AS d
FROM documents ORDER BY d LIMIT 10Output:
Vector Index Scan (HNSW, count=50000)— index is being usedFull table scan— no index, falling back to brute force
- Build index after bulk inserts — insert all vectors first, then CREATE VECTOR INDEX
- Use
ExecuteBatchSQLfor bulk inserts — ensures data persists to storage engine - Flush after writes — call
db.Flush(); db.ForceSave();before querying - Normalize embeddings — most LLM embeddings are already normalized; use cosine distance
- Benchmark with your data — recall and latency vary by dataset distribution
Last updated: 2026-02