Reproducible benchmark · recognized dataset · three systems

pgContext vs pgvector vs Qdrant on GloVe-100-angular

Approximate nearest-neighbor search, 1,183,514 × 100-dimensional vectors, cosine distance, matched Docker deployment

Dataset
ann-benchmarks glove-100-angular — 1,183,514 train, 10,000 test, 100 dims
Ground truth
Dataset-provided exact neighbors
Metric
Angular / cosine · recall@10
Deployment
All three systems run in Docker and are queried the same way, so none is handicapped by the test setup
Index
HNSW, m = 16, ef_construction = 64; 8-way parallel build for the PostgreSQL engines
Hardware
Apple M4 Pro · PostgreSQL 17 · pgvector 0.8.5 · Qdrant 1.18.2 · NEON kernels
Date
2026-07-20 · single trial, warm cache, single client

Two findings, stated plainly. Against pgvector, pgContext matches recall at every setting and serves it 3.8–5.3× faster. Against Qdrant — now that all three run in the same Docker setup, so the comparison is like-for-like — Qdrant is a strong, mature system that leads at high recall: 0.963 recall@10 at 2.38 ms. That lead comes from searching many graph segments in parallel per query, not from a better graph, and it is exactly the capability pgContext's roadmap adds next.

pgContext pgvector Qdrant points labeled by ef_search
Figure 1. Latency–recall frontier (log latency axis); lower and further right is better. pgContext dominates pgvector outright. Qdrant reaches higher recall at comparable latency — the upper-right region a single-threaded scan does not reach in this ef_search range.
pgContext pgvector Qdrant
Figure 2. Recall@10 against search effort. pgContext and pgvector track the same graph-quality curve; Qdrant reads higher at each nominal ef_search because it applies that setting per segment (see the effort note below).

Results

Table 1. Recall@10 and median (p50) query latency, milliseconds.
ef_search pgContextp50 pgvectorp50 Qdrantp50
640.7310.670.7502.560.8391.30
1280.8100.940.8204.140.8931.48
2560.8681.460.8707.090.9341.91
5120.9102.440.91012.970.9632.38
Table 2. Index build time (1.18M rows).
SystemBuildSegments
Qdrant19.9 s7
pgvector50.3 s1
pgContext88.0 s1
Reading the pgvector column. Identical vectors, identical HNSW parameters, one shared PostgreSQL container — the only variable is the index engine. pgContext returns the same recall 3.8–5.3× faster.
Comparing search effort fairly. Qdrant splits the collection into 7 independent segments, each with its own HNSW graph, and applies the ef_search setting to every segment. So one Qdrant query at ef 64 actually explores roughly 7× as many candidates as one pgContext query at ef 64 — closer to pgContext at ef 512. Matched that way, pgContext's single graph is slightly more accurate for the same work (0.910 vs 0.839 recall), but Qdrant searches its 7 segments in parallel and so returns results faster (1.30 ms vs 2.44 ms), and can reach 0.963 recall where one graph searched on a single thread cannot. The difference is parallelism per query, not graph quality — and adding that same parallelism (splitting an index into segments searched together) is the next step on pgContext's roadmap.

Method & reproducibility

  • Recall is measured against the dataset's shipped ground-truth neighbors (top-10 intersection). Every PostgreSQL query plan is verified to use the HNSW index.
  • All three systems run in Docker and are queried the same way, so none pays a connection cost the others avoid.
  • Latency is per-query p50 over 10,000 queries, warm cache, single client, after warmup. Single trial on an Apple M4 Pro with NEON distance kernels; absolute numbers are machine-specific. x86 AVX2 kernels exist but are not yet performance-measured.
  • Harness, Dockerfile, and archived result JSON: benchmarks/pgvector_comparison/. Full lane-by-lane discussion: docs/benchmarks/pgvector.md.