GenericVectorBuilder

Benchmark

Summary and all runs

Vector engine benchmark: eshoponweb

Run started 7 Oct 2026, 20:51 UTC. Data: eShopOnWeb, 524 vectors. Queries: 20 labelled questions.

Used by: published-2026-10-08 (v8, basis)

Machine: CPU Intel(R) Xeon(R) CPU E5-1620 v3 @ 3.50GHz; Logical CPUs 8; RAM (GiB) 62.7; OS Ubuntu 24.04.5 LTS, kernel 6.8.0-142-generic; Governor performance; Partition client 0-1,4-5; engines 2-3,6-7; Release build; Code commit 9b924200abc3f2c3e41c2a9520e5c7bb16748e6c; Exact mode seconds 60.

Warm-up, as this run's notes record it: Warm-up and settle check, untimed, right before every timed pass: the pass's own search at the pass's own number of searchers for at least 15 s and at least 20 searches (at most 120 s), read in windows of at least 2 s and 100 searches; then a 3 s trial of the same pass.

Clock: pinned true; pinnedMhz 3500; ceilingBeforeMhz 3600; noTurbo atEnd=1, before=0, during=1; uncoreMsr620 atEnd=0x1e1e, before=0xc1e, during=0x1e1e; throttle atEnd=coreEvents=0, packageEvents=0, perCpu=coreCount=0, cpu=0, packageCount=0, packageId=0, siblings=0,4,coreCount=0, cpu=1, packageCount=0, packageId=0, siblings=1,5,coreCount=0, cpu=2, packageCount=0, packageId=0, siblings=2,6,coreCount=0, cpu=3, packageCount=0, packageId=0, siblings=3,7,coreCount=0, cpu=4, packageCount=0, packageId=0, siblings=0,4,coreCount=0, cpu=5, packageCount=0, packageId=0, siblings=1,5,coreCount=0, cpu=6, packageCount=0, packageId=0, siblings=2,6,coreCount=0, cpu=7, packageCount=0, packageId=0, siblings=3,7, readUtc=2026-10-07T22:14:12.449Z, atStart=coreEvents=0, packageEvents=0, perCpu=coreCount=0, cpu=0, packageCount=0, packageId=0, siblings=0,4,coreCount=0, cpu=1, packageCount=0, packageId=0, siblings=1,5,coreCount=0, cpu=2, packageCount=0, packageId=0, siblings=2,6,coreCount=0, cpu=3, packageCount=0, packageId=0, siblings=3,7,coreCount=0, cpu=4, packageCount=0, packageId=0, siblings=0,4,coreCount=0, cpu=5, packageCount=0, packageId=0, siblings=1,5,coreCount=0, cpu=6, packageCount=0, packageId=0, siblings=2,6,coreCount=0, cpu=7, packageCount=0, packageId=0, siblings=3,7, readUtc=2026-10-07T20:51:06.231Z; toleranceBp 100; rule every CPU's clock is read from /sys/devices/system/cpu/cpuN/cpufreq/scaling_cur_freq every 250 ms; each CPU's median over a pass is taken, then the median of those medians per CPU group: the engine CPUs (2-3,6-7) and the client CPUs (0-1,4-5) are judged as two groups; a pass is 'clock off' (clockOff) when a group's median differs from the pinned clock by more than 100 bp (1%), and 'clock not read' (clockRead false) when a group has no reading; either counts as not at the pinned clock. Limited power: a CPU's median hides off-clock readings while they are fewer than half of its readings, and the median of 4 CPU medians hides 1 CPU(s) off the clock for the whole pass. This rule reads the kernel's figures only; the independent check of the clock is APERF/MPERF, read by the run's observer outside this tool. Thermal throttle counters (thermal_throttle/core_throttle_count and package_throttle_count of every CPU) are read at the start and the end of the run and at each target's start, first warm-up line and end, never by the clock sampler and never inside a timed pass; the core counter is counted once per physical core (thread_siblings_list) and the package counter once per package (physical_package_id), a rise on any CPU counts, a counter that went down counts as a rise, and only a rise against no rise means anything. They count thermal events only: no power-limit counters are read, so power capping (RAPL) and AVX clock drops are not seen..

Enginep50 (ms)Searches per second, one searcherSearches per second, eight searchers at onceExact mode p50 (ms)Client CPU per search, one searcher (ms)Recall in hitsFlags
Chroma2.08475791-0.47200 of 200
ClickHouse4.842003366.630.85199 of 200UnNh
DuckDB3.642725814.164.98200 of 200
Elasticsearch1.317492,7141.250.46200 of 200Ix
MariaDB0.631,5495,9271.630.49200 of 200
Milvus2.254231,270-0.55200 of 200
MongoDB Atlas Local1.387092,1301.230.31200 of 200
OpenSearch1.984941,5162.550.46200 of 200
Oracle 23ai Free0.921,0602,8322.450.87200 of 200
Qdrant (HNSW)0.921,0743,4770.900.73200 of 200
Qdrant (exact)0.891,1033,280-0.74200 of 200
Redis
redis holds its data in memory: its saved docs page says 'Redis is an in-memory but persistent on disk database', and its compose file sets save "300 1" and appendonly no.
sources
  • doc doc:design/engine-docs/redis-faq-2026-10-07.html#Redis is an in-memory but persistent on disk database = Redis is an in-memory but persistent on disk database
  • file deploy/engines/redis.compose.yaml#--save "300 1" = --save "300 1"
  • file deploy/engines/redis.compose.yaml#--appendonly no = --appendonly no
0.372,6185,2370.370.54200 of 200
SQL Server 20253.94248580-1.22200 of 200
SQL Server 2025 + DiskANN3.582756953.621.03193 of 200
Typesense4.182365735.640.53200 of 200
Vespa1.915051,5711.890.92200 of 200
Weaviate5.33167306-0.57200 of 200
pgvector0.841,1584,0952.180.67200 of 200
sqlite-vec1.925141,1311.922.06200 of 200

Engines are listed in alphabetical order. The table does not rank them.

A dash means the table has no figure there.

The small markers after an engine name are flags. Hover a marker for its evidence, or read the list below the tables.

  • Un unsettled-target The engine did not pass its own settle check before a timed pass. The evidence names the check.
  • Ix index-not-ready The engine did not report a finished index after the load or after the searches.
  • Nh not-held The run recorded a timed pass of this engine as NOT HELD: the engine was still changing when it was timed. The evidence gives the figures. The row is shown, not ranked.
Evidence behind the flags
  • ClickHouse unsettled-target latency had NOT settled when timing began for default@8 (each by its own warm-up at its own concurrency and a trial of the same pass within 10% of the warm-up's settled figure, and the timed pass itself within 10% of it). default@8: warm-up 15.0 s and 6,523 searches (0 failed) at 8 searchers; QPS of the last windows 453, 468, 442 (windows of at least 2 s and 100 searches) (its last 3 windows differed by more than 5%); trial of 1,267 searches in 3.0 s at 8 searchers: 421 QPS against the settled 453 QPS, 8% apart (limit 10%), so the warm-up was EXTENDED once (50.1 s and 20,780 searches (0 failed) at 8 searchers; QPS of its older 10 windows 408 (windows 472, 462, 437, 371, 441, 464, 414, 410, 340, 266) and of its newer 10 418 (windows 295, 431, 418, 408, 444, 472, 407, 408, 432, 470), 2.6% apart (limit 5%) (windows of at least 2 s and 100 searches)); second trial of 1,222 searches in 3.0 s at 8 searchers: 404 QPS against the settled 418 QPS, 3% apart (limit 10%), inside its newer windows' range 295 to 472 QPS; settled after the extension; the timed pass 336 QPS, 24% from the settled 418 QPS (limit 10%), NOT HELD: the engine was still changing when it was timed; default@1: warm-up 15.0 s and 2,629 searches (0 failed) at 1 searcher; p50 of the last windows 5.090, 5.332, 4.942 ms (windows of at least 2 s and 100 searches) (its last 3 windows differed by more than 5%); trial of 519 searches in 3.0 s at 1 searcher: p50 5.081 ms against the settled p50 5.090 ms, 0% apart (limit 10%), so the warm-up was EXTENDED once (30.0 s and 5,755 searches (0 failed) at 1 searcher; p50 of its older 6 windows 4.954 ms (windows 5.366, 5.484, 5.357, 4.829, 4.829, 4.833) and of its newer 6 4.847 ms (windows 4.865, 4.832, 4.843, 4.842, 4.856, 4.840), 2.2% apart (limit 5% or 0.1 ms; 0.106 ms) (windows of at least 2 s and 100 searches)); second trial of 559 searches in 3.0 s at 1 searcher: p50 5.137 ms against the settled p50 4.847 ms, 6% apart (limit 10%), outside its newer windows' range 4.832 to 4.865 ms; settled after the extension; the timed pass p50 4.840 ms, 0% from the settled p50 4.847 ms (limit 10% or 0.1 ms; 0.008 ms); exact: warm-up 15.0 s and 2,155 searches (0 failed) at 1 searcher; p50 of the last windows 6.947, 6.647, 6.588 ms (windows of at least 2 s and 100 searches) (its last 3 windows differed by more than 5%); trial of 423 searches in 3.0 s at 1 searcher: p50 6.698 ms against the settled p50 6.647 ms, 1% apart (limit 10%), so the warm-up was EXTENDED once (30.0 s and 4,405 searches (0 failed) at 1 searcher; p50 of its older 6 windows 6.629 ms (windows 6.597, 6.601, 6.706, 6.648, 6.614, 6.629) and of its newer 6 6.631 ms (windows 6.650, 6.572, 6.606, 6.609, 6.724, 6.645), 0.0% apart (limit 5% or 0.1 ms; 0.002 ms) (windows of at least 2 s and 100 searches)); second trial of 442 searches in 3.0 s at 1 searcher: p50 6.621 ms against the settled p50 6.631 ms, 0% apart (limit 10%), inside its newer windows' range 6.572 to 6.724 ms; settled after the extension; the timed pass p50 6.630 ms, 0% from the settled p50 6.631 ms (limit 10% or 0.1 ms; 0.001 ms). Its numbers may still include warm-up or a change the engine was still going through; rerun before quoting them.
  • ClickHouse not-held default@8: the timed pass 336 QPS, 24% from the settled 418 QPS (limit 10%), NOT HELD
  • Elasticsearch index-not-ready afterLoad: the engine's index state read not ready, 0 of 524 vectors indexed
  • Elasticsearch index-not-ready afterSearch: the engine's index state read not ready, 0 of 524 vectors indexed

Full results

This report is printed as the run wrote it, except for any sentence a note above it says was left out. This page does not check the engine texts in it. The summary page's facts table gives the label of each fact it uses.

Run 2026-10-07T20:51:06Z (run-all). Command line:

~/gvb-work/v8-run/bin/GenericVectorBuilder.Bench.dll run-all --pipeline eshoponweb --queries golden --seed 801 --out ~/ForClaude/GenericVectorBuilder/bench-results --repo ~/ForClaude/GenericVectorBuilder
engineindexload rows/sp50 msp95 msp99 msQPS@1QPS@8client CPU ms/search@1client CPU ms/search@8recall@10nDCG@10RAMdisk
oracleHNSW in-memory neighbor graph NEIGHBORS=16 EFCONSTRUCTION=128, EFSEARCH=100 per query, cosine; exact mode = FETCH EXACT FIRST (full scan); Oracle Free caps itself at 2 CPUs (cpu_count 2 in V$PARAMETER, edition FREE in V$INSTANCE, 8 host CPUs in V$OSSTAT NUM_CPUS; the 2 CPU thread limit is Oracle's documented Free edition limit)1,7410.921.101.791059.92831.80.870.641.0000.5182.14 GiB0 B
mongodbvectorSearch index, HNSW maxEdges=16 numEdgeCandidates=128, float32 binData, cosine, numCandidates=20x hits (min 100); exact mode = $vectorSearch exact:true3,0371.381.611.96708.52130.40.310.281.0000.518694.8 MiB-
redisHNSW TYPE FLOAT32 M=16 EF_CONSTRUCTION=128, EF_RUNTIME=100 per query, cosine; exact mode = FLAT index built on first exact query11,2340.370.480.712617.85237.00.540.361.0000.518253.5 MiB2.43 MiB
typesenseHNSW float32 (hnswlib), m=16, ef_construction=128, cosine; search k=top, ef=100; exact mode = filter ordinal:>=0 with flat_search_cutoff; index held in memory8884.184.575.79235.8572.70.530.591.0000.518116.6 MiB1.86 MiB
clickhousevector_similarity HNSW cosineDistance, quantization bf16, M=16 ef_construction=128, hnsw_candidate_list_size_for_search=256, rescoring off; exact mode = full scan with skip indexes off3,1384.845.677.55200.1336.50.851.060.9950.528975.2 MiB5.29 GiB
sqlexact VECTOR_DISTANCE cosine, no vector index (full scan)5643.944.495.16248.4580.41.221.231.0000.518530.8 MiB5.15 MiB
opensearchfaiss HNSW float32, no compression, m=16, ef_construction=128, cosinesimil; search k=top, ef_search=100; 1 shard, 0 replicas; graph built at any segment size (approximate_threshold=0); force-merged to one segment after the load3091.982.262.81493.81516.00.460.511.0000.5182.55 GiB9.65 MiB
milvusHNSW M=16 efConstruction=128, ef=100, metric COSINE, Strong consistency searches; approximate only (no exact mode)1,1682.252.803.70423.01270.10.550.631.0000.518187.4 MiB10.86 MiB
sql-diskannDiskANN (preview) via VECTOR_SEARCH, cosine, build {"StartId":"306", "L":"48", "M":"8", "R":"48"}; the exact mode scans the same table6083.584.095.16275.0695.31.031.100.9650.526529.8 MiB4.52 MiB
duckdbHNSW (vss extension) FLOAT[n] metric=cosine m=16 ef_construction=128, ef_search=100 per connection, persistent (hnsw_enable_experimental_persistence=true, checkpoint_threshold=256MB); exact mode = array_cosine_similarity sequential scan; score = 1 - cosine distance; searches run concurrently, one connection per searcher (opened as searchers arrive, at most 32), writes run one at a time and never overlap a search1,5003.644.015.22271.6580.94.986.831.0000.518-2.9 MiB
qdrant-hnswHNSW m=16 ef_construct=100, hnsw_ef=server default, cosine; indexing_threshold_kb 1 and full_scan_threshold_kb 10 (server defaults are 10,000 each) so a small collection builds and walks its graph5,4120.921.021.481074.13477.20.730.521.0000.51836.57 MiB164.1 MiB
weaviateHNSW maxConnections(M)=16 efConstruction=128, ef=-1 (dynamic: limit x 8 clamped 100..500), cosine, no quantization; approximate only (no exact mode)1,0045.3310.811.8166.6306.20.570.621.0000.51894.94 MiB2.96 MiB
pgvectorHNSW vector_cosine_ops m=16 ef_construction=128, hnsw.ef_search=100 per query, float32 vector(n), cosine; exact mode = same query with index scans off (sequential scan)6180.841.001.271158.44095.10.670.541.0000.518101.6 MiB7.56 MiB
vespaHNSW float32 tensor, prenormalized-angular (cosine), max-links-per-node=16, neighbors-to-explore-at-insert=128; search targetHits=top, ef=100 via exploreAdditionalHits; exact mode = approximate:false; vectors held in memory2641.912.412.73505.01570.70.920.881.0000.5182.77 GiB9.84 MiB
elasticsearchHNSW float32, no quantization, m=16, ef_construction=128, cosine; search k=top, num_candidates=100; 1 shard, 0 replicas; force-merged to one segment after the load (at 1,024 dimensions a segment under 1,043 vectors gets no graph)4641.311.471.73749.52714.10.460.491.0000.5182.6 GiB2.39 MiB
mariadbVECTOR INDEX (HNSW variant) DISTANCE=cosine, M=16 (no ef_construction setting exists), mhnsw_ef_search=100 per statement (the ef 100 most engines here use, so the search effort matches; MariaDB's own default is 20; recall@10 at ef 100 falls as the set grows (random 1024-dimension vectors, measured 2026-10-04: 0.99 at 524, 0.89 to 0.92 at 2,000; an earlier run gave about 0.09 at 100,000 [Correction 10])), mhnsw_max_cache_size 4G; exact mode = IGNORE INDEX full scan1,0420.630.710.871549.25927.20.490.381.0000.518156.1 MiB22.01 MiB
chromaHNSW M=16 ef_construction=128, ef_search=100 (Chroma default), cosine; approximate only (no exact mode)6992.082.362.62475.4791.00.470.471.0000.51849.21 MiB414.16 KiB
sqlitevecvec0 brute-force scan, no ANN index (exact), float32, cosine distance, default chunk_size=1024; score = 1 - cosine distance; searches run concurrently, one WAL reader connection per searcher (opened as searchers arrive, at most 32), writes run one at a time and may overlap searches3,6981.922.152.35514.21130.82.063.531.0000.518-9.72 MiB
qdrantexact scan: the builder's sink sends exact=true on every search, so no HNSW graph is used whether or not Qdrant has built one (see the index state)8,5020.891.011.491103.03279.80.740.531.0000.51835.73 MiB196.08 MiB

Details per target

Oracle 23ai Free oracle
MongoDB Atlas Local mongodb
Redis redis
Typesense typesense
ClickHouse clickhouse
SQL Server 2025 sql
OpenSearch opensearch
Milvus milvus
SQL Server 2025 + DiskANN sql-diskann
DuckDB duckdb
Qdrant (HNSW) qdrant-hnsw
Weaviate weaviate
pgvector
Vespa vespa
Elasticsearch elasticsearch
MariaDB mariadb
Chroma chroma
sqlite-vec sqlitevec
Qdrant (exact) qdrant

Notes

Raw files

The raw files below hold the recorded statements that the corrections above refer to, as written.