shrinkvec report
dbpedia-openai-1536. DBpedia entities embedded with OpenAI text-embedding-ada-002. MIT.
vectors 100,000dimensions 1536metric cosinequeries 1,000k 10seed 42
Recommendation for recall@10 ≥ 0.95
VectorChord vchordrq, codes only (rerank_in_table), vchordrq.probes=20: recall@10 0.957, p50 1.69 ms, index 30.6 MiB (321 bytes per vector). The smallest index that meets 0.95.
CREATE EXTENSION IF NOT EXISTS vchord CASCADE;
CREATE INDEX ON items USING vchordrq (embedding vector_cosine_ops) WITH (options = $$
rerank_in_table = true
residual_quantization = true
[build.internal]
lists = [200]
spherical_centroids = true
$$);
SET vchordrq.probes = '20';
SELECT id FROM items ORDER BY embedding <=> $1 LIMIT 10;Codec lab: what the codes can do
Every option scans all codes exactly, so recall reflects the compression itself, not an index. Ground truth is exact search in float64. Hollow markers rerank the best candidates with a more precise codec.
| pipeline | what it is | recall@10 | true top-10 in candidates | bytes/vector searched | bytes/vector stored | vs float32 | in Postgres |
|---|---|---|---|---|---|---|---|
float32 | Full precision (float32) | 1.000 | 6,144 | 6,144 | 1.0x | pgvector vector | |
halfvec | Half precision (float16) | 0.999 | 3,072 | 3,072 | 2.0x | pgvector halfvec | |
int8 | 8-bit scalar, per-dimension range | 0.994 | 1,536 | 1,536 | 4.0x | not in pgvector | |
tq-mse-4 | TurboQuant 4-bit (mse) | 0.953 ± 0.001 | 772 | 772 | 8.0x | pg_turbovec | |
tq-prod-4 | TurboQuant 4-bit (prod) | 0.817 ± 0.003 | 772 | 772 | 8.0x | pg_turbovec | |
tq-mse-3 | TurboQuant 3-bit (mse) | 0.916 ± 0.002 | 580 | 580 | 10.6x | pg_turbovec | |
tq-mse-2 | TurboQuant 2-bit (mse) | 0.850 ± 0.000 | 388 | 388 | 15.8x | pg_turbovec | |
rabitq-4 | RaBitQ 4-bit (extended) | 0.972 ± 0.001 | 776 | 776 | 7.9x | VectorChord vchordrq | |
rabitq-1 | RaBitQ 1-bit | 0.834 ± 0.003 | 200 | 200 | 30.7x | VectorChord vchordrq | |
binary | 1 sign bit per dimension, Hamming distance | 0.707 | 192 | 192 | 32.0x | pgvector binary_quantize | |
tq-mse-3>tq-mse-8 | TurboQuant 3-bit (mse), rerank top 100 with TurboQuant 8-bit (mse) | 0.996 ± 0.000 | 1.000 | 580 | 2,120 | 10.6x | pg_turbovec |
rabitq-1>float32 | RaBitQ 1-bit, rerank top 100 with Full precision (float32) | 0.999 ± 0.000 | 0.999 | 200 | 6,344 | 30.7x | VectorChord vchordrq |
binary>float32 | 1 sign bit per dimension, Hamming distance, rerank top 100 with Full precision (float32) | 0.992 | 0.992 | 192 | 6,336 | 32.0x | pgvector binary_quantize |
Postgres: real indexes on this server
PostgreSQL 18.3 (Debian 18.3-1.pgdg12+1) with vector 0.8.2, vchord 1.1.1. Each index is built on a copy of the vectors, then every query runs once to warm up and once timed, from one client. Latency includes the round trip.
| index | setting | recall@10 | p50 ms | p95 ms | QPS (1 client) | index MiB | index B/vector | build s |
|---|---|---|---|---|---|---|---|---|
hnsw-vector | hnsw.ef_search=40 | 0.941 | 1.74 | 2.23 | 570 | 781.3 | 8,192 | 83.1 |
hnsw-vector | hnsw.ef_search=80 | 0.959 | 2.34 | 3.07 | 423 | 781.3 | 8,192 | 83.1 |
hnsw-vector | hnsw.ef_search=160 | 0.972 | 3.55 | 4.51 | 288 | 781.3 | 8,192 | 83.1 |
hnsw-vector | hnsw.ef_search=320 | 0.982 | 5.83 | 8.60 | 169 | 781.3 | 8,192 | 83.1 |
hnsw-halfvec | hnsw.ef_search=40 | 0.940 | 1.73 | 2.27 | 569 | 390.6 | 4,096 | 70.6 |
hnsw-halfvec | hnsw.ef_search=80 | 0.957 | 2.31 | 3.89 | 398 | 390.6 | 4,096 | 70.6 |
hnsw-halfvec | hnsw.ef_search=160 | 0.969 | 3.71 | 5.41 | 264 | 390.6 | 4,096 | 70.6 |
hnsw-halfvec | hnsw.ef_search=320 | 0.978 | 5.35 | 7.10 | 188 | 390.6 | 4,096 | 70.6 |
hnsw-binary-rerank | candidates=100 | 0.963 | 2.83 | 4.97 | 320 | 47.9 | 502 | 32.6 |
hnsw-binary-rerank | candidates=200 | 0.977 | 4.98 | 7.07 | 194 | 47.9 | 502 | 32.6 |
hnsw-binary-rerank | candidates=400 | 0.986 | 7.65 | 9.80 | 129 | 47.9 | 502 | 32.6 |
hnsw-binary-rerank | candidates=800 | 0.994 | 13.69 | 16.52 | 73 | 47.9 | 502 | 32.6 |
vchordrq | vchordrq.probes=4 | 0.854 | 1.04 | 1.42 | 916 | 812.4 | 8,519 | 13.7 |
vchordrq | vchordrq.probes=10 | 0.916 | 1.31 | 1.77 | 735 | 812.4 | 8,519 | 13.7 |
vchordrq | vchordrq.probes=20 | 0.949 | 1.71 | 2.00 | 578 | 812.4 | 8,519 | 13.7 |
vchordrq | vchordrq.probes=40 | 0.981 | 2.58 | 3.27 | 380 | 812.4 | 8,519 | 13.7 |
vchordrq-codes | vchordrq.probes=4 | 0.853 | 1.07 | 1.26 | 918 | 30.6 | 321 | 10.6 |
vchordrq-codes | vchordrq.probes=10 | 0.920 | 1.30 | 1.51 | 758 | 30.6 | 321 | 10.6 |
vchordrq-codes | vchordrq.probes=20 | 0.957 | 1.69 | 2.01 | 583 | 30.6 | 321 | 10.6 |
vchordrq-codes | vchordrq.probes=40 | 0.985 | 2.43 | 2.87 | 405 | 30.6 | 321 | 10.6 |
Reproduce
pip install 'shrinkvec[pg,datasets]'
shrinkvec run --dataset dbpedia-openai-1536 -n 100000 --repeats 3 --out docs/results/dbpedia-openai-1536