2 of 3 lanes measured
VectorDBBench
Qualified vector search quality, throughput, serial latency, serving demand, and database volume across operating points.
vector-vdbbench-50k
complete50K
Reviewed 50K vector search quality, throughput, latency, serving demand, and valid database volume evidence.
Methodology scope · protocol · interpretation
VectorDBBench methodology
Antfly subject and execution lane
Circus accepts an explicit subject manifest containing the asserted release tag,
commit, and Linux/arm64 Antfly binary. It records the binary path, SHA-256,
architecture, and observed --version output, while treating the supplied tag
and commit as release metadata. The honed matrix mounts that binary read-only
into Antfly’s Linux container. Every comparison engine runs in the same private
Docker network; no native macOS database is mixed into this benchmark.
The public benchmarks/VectorDBBench checkout remains clean. Circus-owned
parameter selection, compatibility fixes, query partitioning, and diagnostics
live in benchmarks/VectorDBBench-harness and are loaded as a runtime overlay.
Workloads and query protocol
The publication matrix contains six engines on both upstream workloads:
Performance1536D50K: 50,000 vectors with 1,536 dimensions.Performance768D1M: 1,000,000 vectors with 768 dimensions.
For each workload, the 1,000 upstream query/ground-truth pairs are ranked by a fixed SHA-256 seed and split into 200 calibration queries and 800 publication queries. Their exact overlap is zero; both partition fingerprints are retained.
Each engine/workload trial has one fresh service and one fixed index lifecycle:
- Start lifecycle memory sampling before indexing.
- Load and build the index once using fixed construction parameters.
- Sweep only the query-time parameter on the 200-query calibration partition.
- Freeze the lowest setting reaching recall 0.955. If none qualifies, retain the highest-recall setting and mark it below the qualification floor.
- Without rebuilding, run the selected setting on the untouched 800-query partition at 1, 5, 10, 20, and 30 concurrent clients for 30 seconds each, followed by the upstream serial quality and latency pass.
- Capture one diagnostic post-workload storage observation, gracefully stop and verify the service, capture final storage, retain the artifacts, and only then reclaim the engine’s data.
The 50K and 1M settings are selected independently because the larger graph may need a higher search setting. Publication results never influence selection. A row below the 0.950 publication floor remains visible and is not silently retuned or omitted. No process-restart measurement is published.
Tuned and fixed parameters
| Engine | Fixed index/build parameters | Query-time selection |
|---|---|---|
| Antfly | AKNN, one shard, packed float32 queries | search_effort sweep |
| Milvus | HNSW M=16, efConstruction=256 | ef_search sweep |
| Chroma | HNSW M=16, efConstruction=256 | fixed ef_search=256 qualification |
| Weaviate | HNSW maxConnections=16, efConstruction=256 | ef sweep |
| pgvector | HNSW m=16, ef_construction=128 | ef_search sweep |
| Elasticsearch | HNSW m=16, ef_construction=100 | num_candidates sweep |
The final report prints the selected value for every workload and engine.
Weaviate and Elasticsearch are local containers; their upstream adapter class
names (WeaviateCloud and ElasticCloud) do not describe the deployment.
Memory
The table reports both memory views. Demand working set is the maximum sampled
cgroup-v2 anon + shmem from before indexing through readiness, calibration,
and publication queries; it excludes reclaimable file cache and follows
MEASURING_ANTFLY.md. Cache-inclusive peak is the maximum concurrently
sampled sum of memory.current across the complete service stack and includes
file cache and cgroup-charged kernel memory. Sampling runs every 200 ms and
retains the timeline. Kernel memory.peak is also retained as an exact peak for
a single cgroup, or a conservative sum of independent peaks for a
multi-container stack.
Honed publication requires three fresh lifecycles per engine/workload. The headline table shows each arithmetic mean; detail artifacts retain every observation, the mean, and the individual-trial range. A trial is rejected if the canonical sampler reports a failure, misses any container in the service stack, starts after indexing, stops before publication finishes, or leaves a timeline gap above five seconds. Peak throughput, its concurrency, C=30 throughput and concurrent p99, recall, nDCG, serial latency, and memory are arithmetic means of the three compatible lifecycle observations. Raw curves and ranges remain retained. The selected setting and protocol identity must match across all three trials.
The complete Antfly standalone container is measured, so all Antfly processes and threads in that cgroup are included. Termite is not part of this workload because VectorDBBench supplies precomputed vectors.
Persistent database volume
Every publication engine receives one newly created, run-labeled Docker
local volume at its complete persistence root. The digest-pinned Alpine probe
captures whole-volume allocated and apparent bytes once at empty, ready,
and post_workload, then at the verified post-stop final barrier.
Host-path/no-network Antfly is rejected for comparative storage. Pre-stop
background compaction remains diagnostic and does not gate the post-stop metric.
Final allocated bytes are the headline. The same three construction lifecycles
define performance, memory, and storage aggregates. Every value, arithmetic
mean, range, receipt, and barrier sample remains under raw/storage.
Internal Antfly scale diagnostic
make vdbbench-scale-fast and make vdbbench-scale-big run an Antfly-only
diagnostic beside the cross-engine publication matrix. The fast profile uses
20K, 40K, 60K, 80K, and 100K vectors; the big profile uses 200K, 400K, 600K,
800K, and 1M vectors. make vdbbench-scale selects both. All points use 768
dimensions and the same 200-query calibration partition, disjoint 800-query
held-out partition, serial pass, and 1/5/10/20/30-client 30-second capped sweep
described above.
The diagnostic has exactly two fresh index lifecycles per point. Trial one runs
from the smallest to largest selected corpus; trial two reverses that order.
This controls simple order effects without claiming the three-lifecycle
publication cohort. The aggregate and every row set
publication_eligible: false, retain both raw values plus their arithmetic
mean and range, and state the two-lifecycle caveat. Scale results must not be
merged into the honed cross-engine cohort.
The source corpus is the pinned upstream Cohere 768-dimensional 1M-vector fixture. Circus verifies its immutable source files, computes exact cosine top-k ground truth against deterministic prefix sizes, and writes explicit custom-dataset manifests. A streaming iterator is bounded by the manifest’s declared prefix size; a smaller point cannot consume rows belonging to a larger shared Parquet file.
Each point/trial is an immutable publication-cache cell with workload-specific
dataset identity. A cache miss receives one fresh retry, never an in-place
resume. Only a complete positive lifecycle or a classified held-out
capacity/rejection result can be promoted. The canonical diagnostic artifact
retains every cell’s receipt/cache reference, query-partition digest, selected
search effort, request-error evidence, memory timeline, final allocated volume,
and insert_duration + optimize_duration build time.
Output and retention
The canonical artifact contains report.md, normalized result.json,
provenance, a receipt, selected settings, the raw capped-concurrency JSON/CSV
diagnostic, memory rows and timelines, logs, supervisor/status evidence, and
upstream result JSON. Standard publication runs VectorDBBench’s increasing
concurrency progression at 1, 5, 10, 20, and 30 clients. Normalized rows report
peak QPS and its concurrency as well as qps_c30 and p99_c30_ms.
A lifecycle that completes the selected-setting, held-out capped sweep but has
explicit nonzero request rejections is retained as a classified capacity
terminal negative. It is reusable only with the exact concurrency schedule,
query partition, selected setting, full memory timeline, quiet-host assertion,
logs, and supervisor evidence. Setup/index/configuration failures, crashes,
timeouts, partial lifecycles, and unknown failures are never reusable
publication cells.
Database volumes are deleted only after the verified final capture unless an
operator explicitly opts into retaining them for debugging. The retained
storage.json receipt and complete optional-trial evidence are results; the
live volume is not.
Run evidence parameters · variation · provenance
Benchmark parameters
{"concurrency_duration_seconds":30,"concurrency_schedule":[1,5,10,20,30],"query_count":800,"workloads":["1536d-50K"]}Trial variation
[{"engine":"Antfly","ranges":{"demand_working_set":{"maximum":1849.2,"mean":1747.9,"minimum":1649.5,"unit":"MB"},"final_database_volume":{"maximum":356.687872,"mean":356.223659,"minimum":355.975168,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":42.8,"mean":41.633,"minimum":40.8,"unit":"ms"}},"workload":"1536d-50K"},{"engine":"Chroma","ranges":{"demand_working_set":{"maximum":468.9,"mean":463.3,"minimum":455.4,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":176.7,"mean":176.467,"minimum":176.3,"unit":"ms"}},"workload":"1536d-50K"},{"engine":"ElasticCloud","ranges":{"demand_working_set":{"maximum":1871.2,"mean":1861.3,"minimum":1854.6,"unit":"MB"},"final_database_volume":{"maximum":312.430592,"mean":312.380075,"minimum":312.352768,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":13.7,"mean":13.5,"minimum":13.4,"unit":"ms"}},"workload":"1536d-50K"},{"engine":"Milvus","ranges":{"demand_working_set":{"maximum":2577.1,"mean":2490.3,"minimum":2428.4,"unit":"MB"},"final_database_volume":{"maximum":1741.217792,"mean":1741.202773,"minimum":1741.193216,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":10.6,"mean":10.433,"minimum":10.3,"unit":"ms"}},"workload":"1536d-50K"},{"engine":"PgVector","ranges":{"demand_working_set":{"maximum":2303.9,"mean":2303.8,"minimum":2303.7,"unit":"MB"},"final_database_volume":{"maximum":1523.970048,"mean":1523.965952,"minimum":1523.961856,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":35.6,"mean":33.7,"minimum":32.6,"unit":"ms"}},"workload":"1536d-50K"},{"engine":"WeaviateCloud","ranges":{"demand_working_set":{"maximum":965.9,"mean":938.5,"minimum":912.1,"unit":"MB"},"final_database_volume":{"maximum":391.360512,"mean":391.277227,"minimum":391.172096,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":86.5,"mean":85.533,"minimum":83.7,"unit":"ms"}},"workload":"1536d-50K"}]Peer versions
Peer versions were not recorded in the published result fields.
Evidence digests
- Receipt
e599c852ae6c1544b8fd5c41183b3b56fc5a5ef894b2643be94b6da83a550a09- Aggregate
809572e4fcec1c0b928f2f0cf1d7670e3724d06e2a5619852ed1b47eaafa2d3e- Provenance
00db9a59f839aabdd0cc506f6e04b1316b4859b9e9a6de41285f179926741a31- Harness commit
a379ba64c66bfa7f2468d1fcfc78d3e79a32f639
Scope notes
- Chroma 50K storage is unavailable: the harness measured an unused volume rather than the database path persisted by the pinned image.
- Completed
- Antfly
v0.2.0·- Runtime
- 3h 21m 2s
- Platform
- darwin · arm64
- Machine
- Apple M4 Max · 14 CPU · 38.7 GB
- Build
- optimization mode not captured · Zig compiler version not captured
Completion time is taken from the run’s recorded validation time; this older result predates direct finish-time capture.
Chroma 50K storage is unavailable: the harness measured an unused volume rather than the database path persisted by the pinned image.
Peak throughput
Antfly v0.2.0 (56a1b1a5) · darwin-arm64 · Apple M4 Max · 1536d-50K · Higher is better
Serial query p95
Antfly v0.2.0 (56a1b1a5) · darwin-arm64 · Apple M4 Max · 1536d-50K · Lower is better
Demand working set
Antfly v0.2.0 (56a1b1a5) · darwin-arm64 · Apple M4 Max · 1536d-50K · Lower is better
Final database volume
Antfly v0.2.0 (56a1b1a5) · darwin-arm64 · Apple M4 Max · 1536d-50K · Lower is better
Performance, quality, and resource evidence
Antfly v0.2.0 · 56a1b1a5
| Engine | Workload | Recall | nDCG | Peak QPS queries/s | p95 ms | Demand working set MB | Cache-inclusive peak MB | Final database volume MB | Trials |
|---|---|---|---|---|---|---|---|---|---|
| Antfly | 1536d-50K | 0.959 | 0.964 | 1,727.7 | 3 | 1,747.9 MB | 2,213.6 MB | 356.2 MB | 3 |
| Chroma | 1536d-50K | 0.987 | 0.989 | 179.9 | 8.07 | 463.3 MB | 809.2 MB | Not recorded MB | 3 |
| ElasticCloud | 1536d-50K | 0.960 | 0.967 | 4,600.1 | 1.97 | 1,861.3 MB | 4,318.4 MB | 312.4 MB | 3 |
| Milvus | 1536d-50K | 0.963 | 0.970 | 5,643.1 | 1.50 | 2,490.3 MB | 4,565.6 MB | 1,741.2 MB | 3 |
| PgVector | 1536d-50K | 0.969 | 0.975 | 2,432.3 | 4.17 | 2,303.8 MB | 3,265.2 MB | 1,524.0 MB | 3 |
| WeaviateCloud | 1536d-50K | 0.955 | 0.963 | 995.6 | 6.60 | 938.5 MB | 1,553.7 MB | 391.3 MB | 3 |
vector-vdbbench-1m
complete1M
Reviewed 1M vector search quality, throughput, latency, serving demand, and valid database volume evidence.
Methodology scope · protocol · interpretation
VectorDBBench methodology
Antfly subject and execution lane
Circus accepts an explicit subject manifest containing the asserted release tag,
commit, and Linux/arm64 Antfly binary. It records the binary path, SHA-256,
architecture, and observed --version output, while treating the supplied tag
and commit as release metadata. The honed matrix mounts that binary read-only
into Antfly’s Linux container. Every comparison engine runs in the same private
Docker network; no native macOS database is mixed into this benchmark.
The public benchmarks/VectorDBBench checkout remains clean. Circus-owned
parameter selection, compatibility fixes, query partitioning, and diagnostics
live in benchmarks/VectorDBBench-harness and are loaded as a runtime overlay.
Workloads and query protocol
The publication matrix contains six engines on both upstream workloads:
Performance1536D50K: 50,000 vectors with 1,536 dimensions.Performance768D1M: 1,000,000 vectors with 768 dimensions.
For each workload, the 1,000 upstream query/ground-truth pairs are ranked by a fixed SHA-256 seed and split into 200 calibration queries and 800 publication queries. Their exact overlap is zero; both partition fingerprints are retained.
Each engine/workload trial has one fresh service and one fixed index lifecycle:
- Start lifecycle memory sampling before indexing.
- Load and build the index once using fixed construction parameters.
- Sweep only the query-time parameter on the 200-query calibration partition.
- Freeze the lowest setting reaching recall 0.955. If none qualifies, retain the highest-recall setting and mark it below the qualification floor.
- Without rebuilding, run the selected setting on the untouched 800-query partition at 1, 5, 10, 20, and 30 concurrent clients for 30 seconds each, followed by the upstream serial quality and latency pass.
- Capture one diagnostic post-workload storage observation, gracefully stop and verify the service, capture final storage, retain the artifacts, and only then reclaim the engine’s data.
The 50K and 1M settings are selected independently because the larger graph may need a higher search setting. Publication results never influence selection. A row below the 0.950 publication floor remains visible and is not silently retuned or omitted. No process-restart measurement is published.
Tuned and fixed parameters
| Engine | Fixed index/build parameters | Query-time selection |
|---|---|---|
| Antfly | AKNN, one shard, packed float32 queries | search_effort sweep |
| Milvus | HNSW M=16, efConstruction=256 | ef_search sweep |
| Chroma | HNSW M=16, efConstruction=256 | fixed ef_search=256 qualification |
| Weaviate | HNSW maxConnections=16, efConstruction=256 | ef sweep |
| pgvector | HNSW m=16, ef_construction=128 | ef_search sweep |
| Elasticsearch | HNSW m=16, ef_construction=100 | num_candidates sweep |
The final report prints the selected value for every workload and engine.
Weaviate and Elasticsearch are local containers; their upstream adapter class
names (WeaviateCloud and ElasticCloud) do not describe the deployment.
Memory
The table reports both memory views. Demand working set is the maximum sampled
cgroup-v2 anon + shmem from before indexing through readiness, calibration,
and publication queries; it excludes reclaimable file cache and follows
MEASURING_ANTFLY.md. Cache-inclusive peak is the maximum concurrently
sampled sum of memory.current across the complete service stack and includes
file cache and cgroup-charged kernel memory. Sampling runs every 200 ms and
retains the timeline. Kernel memory.peak is also retained as an exact peak for
a single cgroup, or a conservative sum of independent peaks for a
multi-container stack.
Honed publication requires three fresh lifecycles per engine/workload. The headline table shows each arithmetic mean; detail artifacts retain every observation, the mean, and the individual-trial range. A trial is rejected if the canonical sampler reports a failure, misses any container in the service stack, starts after indexing, stops before publication finishes, or leaves a timeline gap above five seconds. Peak throughput, its concurrency, C=30 throughput and concurrent p99, recall, nDCG, serial latency, and memory are arithmetic means of the three compatible lifecycle observations. Raw curves and ranges remain retained. The selected setting and protocol identity must match across all three trials.
The complete Antfly standalone container is measured, so all Antfly processes and threads in that cgroup are included. Termite is not part of this workload because VectorDBBench supplies precomputed vectors.
Persistent database volume
Every publication engine receives one newly created, run-labeled Docker
local volume at its complete persistence root. The digest-pinned Alpine probe
captures whole-volume allocated and apparent bytes once at empty, ready,
and post_workload, then at the verified post-stop final barrier.
Host-path/no-network Antfly is rejected for comparative storage. Pre-stop
background compaction remains diagnostic and does not gate the post-stop metric.
Final allocated bytes are the headline. The same three construction lifecycles
define performance, memory, and storage aggregates. Every value, arithmetic
mean, range, receipt, and barrier sample remains under raw/storage.
Internal Antfly scale diagnostic
make vdbbench-scale-fast and make vdbbench-scale-big run an Antfly-only
diagnostic beside the cross-engine publication matrix. The fast profile uses
20K, 40K, 60K, 80K, and 100K vectors; the big profile uses 200K, 400K, 600K,
800K, and 1M vectors. make vdbbench-scale selects both. All points use 768
dimensions and the same 200-query calibration partition, disjoint 800-query
held-out partition, serial pass, and 1/5/10/20/30-client 30-second capped sweep
described above.
The diagnostic has exactly two fresh index lifecycles per point. Trial one runs
from the smallest to largest selected corpus; trial two reverses that order.
This controls simple order effects without claiming the three-lifecycle
publication cohort. The aggregate and every row set
publication_eligible: false, retain both raw values plus their arithmetic
mean and range, and state the two-lifecycle caveat. Scale results must not be
merged into the honed cross-engine cohort.
The source corpus is the pinned upstream Cohere 768-dimensional 1M-vector fixture. Circus verifies its immutable source files, computes exact cosine top-k ground truth against deterministic prefix sizes, and writes explicit custom-dataset manifests. A streaming iterator is bounded by the manifest’s declared prefix size; a smaller point cannot consume rows belonging to a larger shared Parquet file.
Each point/trial is an immutable publication-cache cell with workload-specific
dataset identity. A cache miss receives one fresh retry, never an in-place
resume. Only a complete positive lifecycle or a classified held-out
capacity/rejection result can be promoted. The canonical diagnostic artifact
retains every cell’s receipt/cache reference, query-partition digest, selected
search effort, request-error evidence, memory timeline, final allocated volume,
and insert_duration + optimize_duration build time.
Output and retention
The canonical artifact contains report.md, normalized result.json,
provenance, a receipt, selected settings, the raw capped-concurrency JSON/CSV
diagnostic, memory rows and timelines, logs, supervisor/status evidence, and
upstream result JSON. Standard publication runs VectorDBBench’s increasing
concurrency progression at 1, 5, 10, 20, and 30 clients. Normalized rows report
peak QPS and its concurrency as well as qps_c30 and p99_c30_ms.
A lifecycle that completes the selected-setting, held-out capped sweep but has
explicit nonzero request rejections is retained as a classified capacity
terminal negative. It is reusable only with the exact concurrency schedule,
query partition, selected setting, full memory timeline, quiet-host assertion,
logs, and supervisor evidence. Setup/index/configuration failures, crashes,
timeouts, partial lifecycles, and unknown failures are never reusable
publication cells.
Database volumes are deleted only after the verified final capture unless an
operator explicitly opts into retaining them for debugging. The retained
storage.json receipt and complete optional-trial evidence are results; the
live volume is not.
Run evidence parameters · variation · provenance
Benchmark parameters
{"concurrency_duration_seconds":30,"concurrency_schedule":[1,5,10,20,30],"query_count":800,"workloads":["768d-1M"]}Trial variation
[{"engine":"Antfly","ranges":{"demand_working_set":{"maximum":8470.1,"mean":8012.1,"minimum":7559.7,"unit":"MB"},"final_database_volume":{"maximum":3874.67264,"mean":3864.095403,"minimum":3858.550784,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":2160.4,"mean":1454.833,"minimum":446.9,"unit":"ms"}},"workload":"768d-1M"},{"engine":"Chroma","ranges":{"demand_working_set":{"maximum":3697.5,"mean":3690.9,"minimum":3684.1,"unit":"MB"},"final_database_volume":{"maximum":3460.145152,"mean":3459.732821,"minimum":3459.108864,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":167.7,"mean":148.833,"minimum":134.9,"unit":"ms"}},"workload":"768d-1M"},{"engine":"ElasticCloud","ranges":{"demand_working_set":{"maximum":2153.7,"mean":2088.4,"minimum":2035.2,"unit":"MB"},"final_database_volume":{"maximum":3153.5104,"mean":3153.36704,"minimum":3153.240064,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":19.5,"mean":19.4,"minimum":19.3,"unit":"ms"}},"workload":"768d-1M"},{"engine":"Milvus","ranges":{"demand_working_set":{"maximum":9128.1,"mean":8882,"minimum":8741.6,"unit":"MB"},"final_database_volume":{"maximum":19268.632576,"mean":19125.717675,"minimum":19050.5984,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":19.6,"mean":19.067,"minimum":18.7,"unit":"ms"}},"workload":"768d-1M"},{"engine":"PgVector","ranges":{"demand_working_set":{"maximum":2321.2,"mean":2318.7,"minimum":2316.4,"unit":"MB"},"final_database_volume":{"maximum":9329.483776,"mean":9329.455104,"minimum":9329.422336,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":78.2,"mean":77.033,"minimum":74.7,"unit":"ms"}},"workload":"768d-1M"},{"engine":"WeaviateCloud","ranges":{"demand_working_set":{"maximum":7339.2,"mean":7325.5,"minimum":7312.2,"unit":"MB"},"final_database_volume":{"maximum":4101.083136,"mean":4096.404139,"minimum":4091.887616,"unit":"MB"},"p99_at_peak_concurrency":{"maximum":77.2,"mean":75.6,"minimum":74.3,"unit":"ms"}},"workload":"768d-1M"}]Peer versions
Peer versions were not recorded in the published result fields.
Evidence digests
- Receipt
9f8d6cf223ac10dc3722f7754de27973f6abc76f395395f48be51b68bcb0d146- Aggregate
2f9311d8e7bc67de25c17d2c13be9a0550b8123e73c3c2429d77a5eec5ca98d3- Provenance
e73dd7891b85149712664234ff01476ee63f88877d62f1c983ea4a320b0f1c54- Harness commit
0719517552c4fad3dc726aff366c2a13e7308fff
- Completed
- Antfly
v0.2.0·- Runtime
- 8h 52m 23s
- Platform
- darwin · arm64
- Machine
- Apple M4 Max · 14 CPU · 38.7 GB
- Build
- optimization mode not captured · Zig compiler version not captured
Peak throughput
Antfly v0.2.0 (56a1b1a5) · darwin-arm64 · Apple M4 Max · 768d-1M · Higher is better
Serial query p95
Antfly v0.2.0 (56a1b1a5) · darwin-arm64 · Apple M4 Max · 768d-1M · Lower is better
Demand working set
Antfly v0.2.0 (56a1b1a5) · darwin-arm64 · Apple M4 Max · 768d-1M · Lower is better
Final database volume
Antfly v0.2.0 (56a1b1a5) · darwin-arm64 · Apple M4 Max · 768d-1M · Lower is better
Performance, quality, and resource evidence
Antfly v0.2.0 · 56a1b1a5
| Engine | Workload | Recall | nDCG | Peak QPS queries/s | p95 ms | Demand working set MB | Cache-inclusive peak MB | Final database volume MB | Trials |
|---|---|---|---|---|---|---|---|---|---|
| Antfly | 768d-1M | 0.960 | 0.964 | 136.7 | 79.67 | 8,012.1 MB | 17,196.2 MB | 3,864.1 MB | 3 |
| Chroma | 768d-1M | 0.956 | 0.962 | 237.6 | 6.90 | 3,690.9 MB | 7,276 MB | 3,459.7 MB | 3 |
| ElasticCloud | 768d-1M | 0.960 | 0.963 | 3,401.8 | 2.93 | 2,088.4 MB | 15,188.7 MB | 3,153.4 MB | 3 |
| Milvus | 768d-1M | 0.962 | 0.965 | 2,570.1 | 1.87 | 8,882 MB | 21,448.9 MB | 19,125.7 MB | 3 |
| PgVector | 768d-1M | 0.966 | 0.970 | 850.9 | 10.70 | 2,318.7 MB | 11,985.1 MB | 9,329.5 MB | 3 |
| WeaviateCloud | 768d-1M | 0.956 | 0.962 | 1,163.6 | 6.63 | 7,325.5 MB | 13,540.2 MB | 4,096.4 MB | 3 |
vector-vdbbench-scale
missingScale
Five-point Antfly scale diagnostics for indexing time, quality, throughput, latency, serving demand, and database volume.
Methodology scope · protocol · interpretation
VectorDBBench methodology
Antfly subject and execution lane
Circus accepts an explicit subject manifest containing the asserted release tag,
commit, and Linux/arm64 Antfly binary. It records the binary path, SHA-256,
architecture, and observed --version output, while treating the supplied tag
and commit as release metadata. The honed matrix mounts that binary read-only
into Antfly’s Linux container. Every comparison engine runs in the same private
Docker network; no native macOS database is mixed into this benchmark.
The public benchmarks/VectorDBBench checkout remains clean. Circus-owned
parameter selection, compatibility fixes, query partitioning, and diagnostics
live in benchmarks/VectorDBBench-harness and are loaded as a runtime overlay.
Workloads and query protocol
The publication matrix contains six engines on both upstream workloads:
Performance1536D50K: 50,000 vectors with 1,536 dimensions.Performance768D1M: 1,000,000 vectors with 768 dimensions.
For each workload, the 1,000 upstream query/ground-truth pairs are ranked by a fixed SHA-256 seed and split into 200 calibration queries and 800 publication queries. Their exact overlap is zero; both partition fingerprints are retained.
Each engine/workload trial has one fresh service and one fixed index lifecycle:
- Start lifecycle memory sampling before indexing.
- Load and build the index once using fixed construction parameters.
- Sweep only the query-time parameter on the 200-query calibration partition.
- Freeze the lowest setting reaching recall 0.955. If none qualifies, retain the highest-recall setting and mark it below the qualification floor.
- Without rebuilding, run the selected setting on the untouched 800-query partition at 1, 5, 10, 20, and 30 concurrent clients for 30 seconds each, followed by the upstream serial quality and latency pass.
- Capture one diagnostic post-workload storage observation, gracefully stop and verify the service, capture final storage, retain the artifacts, and only then reclaim the engine’s data.
The 50K and 1M settings are selected independently because the larger graph may need a higher search setting. Publication results never influence selection. A row below the 0.950 publication floor remains visible and is not silently retuned or omitted. No process-restart measurement is published.
Tuned and fixed parameters
| Engine | Fixed index/build parameters | Query-time selection |
|---|---|---|
| Antfly | AKNN, one shard, packed float32 queries | search_effort sweep |
| Milvus | HNSW M=16, efConstruction=256 | ef_search sweep |
| Chroma | HNSW M=16, efConstruction=256 | fixed ef_search=256 qualification |
| Weaviate | HNSW maxConnections=16, efConstruction=256 | ef sweep |
| pgvector | HNSW m=16, ef_construction=128 | ef_search sweep |
| Elasticsearch | HNSW m=16, ef_construction=100 | num_candidates sweep |
The final report prints the selected value for every workload and engine.
Weaviate and Elasticsearch are local containers; their upstream adapter class
names (WeaviateCloud and ElasticCloud) do not describe the deployment.
Memory
The table reports both memory views. Demand working set is the maximum sampled
cgroup-v2 anon + shmem from before indexing through readiness, calibration,
and publication queries; it excludes reclaimable file cache and follows
MEASURING_ANTFLY.md. Cache-inclusive peak is the maximum concurrently
sampled sum of memory.current across the complete service stack and includes
file cache and cgroup-charged kernel memory. Sampling runs every 200 ms and
retains the timeline. Kernel memory.peak is also retained as an exact peak for
a single cgroup, or a conservative sum of independent peaks for a
multi-container stack.
Honed publication requires three fresh lifecycles per engine/workload. The headline table shows each arithmetic mean; detail artifacts retain every observation, the mean, and the individual-trial range. A trial is rejected if the canonical sampler reports a failure, misses any container in the service stack, starts after indexing, stops before publication finishes, or leaves a timeline gap above five seconds. Peak throughput, its concurrency, C=30 throughput and concurrent p99, recall, nDCG, serial latency, and memory are arithmetic means of the three compatible lifecycle observations. Raw curves and ranges remain retained. The selected setting and protocol identity must match across all three trials.
The complete Antfly standalone container is measured, so all Antfly processes and threads in that cgroup are included. Termite is not part of this workload because VectorDBBench supplies precomputed vectors.
Persistent database volume
Every publication engine receives one newly created, run-labeled Docker
local volume at its complete persistence root. The digest-pinned Alpine probe
captures whole-volume allocated and apparent bytes once at empty, ready,
and post_workload, then at the verified post-stop final barrier.
Host-path/no-network Antfly is rejected for comparative storage. Pre-stop
background compaction remains diagnostic and does not gate the post-stop metric.
Final allocated bytes are the headline. The same three construction lifecycles
define performance, memory, and storage aggregates. Every value, arithmetic
mean, range, receipt, and barrier sample remains under raw/storage.
Internal Antfly scale diagnostic
make vdbbench-scale-fast and make vdbbench-scale-big run an Antfly-only
diagnostic beside the cross-engine publication matrix. The fast profile uses
20K, 40K, 60K, 80K, and 100K vectors; the big profile uses 200K, 400K, 600K,
800K, and 1M vectors. make vdbbench-scale selects both. All points use 768
dimensions and the same 200-query calibration partition, disjoint 800-query
held-out partition, serial pass, and 1/5/10/20/30-client 30-second capped sweep
described above.
The diagnostic has exactly two fresh index lifecycles per point. Trial one runs
from the smallest to largest selected corpus; trial two reverses that order.
This controls simple order effects without claiming the three-lifecycle
publication cohort. The aggregate and every row set
publication_eligible: false, retain both raw values plus their arithmetic
mean and range, and state the two-lifecycle caveat. Scale results must not be
merged into the honed cross-engine cohort.
The source corpus is the pinned upstream Cohere 768-dimensional 1M-vector fixture. Circus verifies its immutable source files, computes exact cosine top-k ground truth against deterministic prefix sizes, and writes explicit custom-dataset manifests. A streaming iterator is bounded by the manifest’s declared prefix size; a smaller point cannot consume rows belonging to a larger shared Parquet file.
Each point/trial is an immutable publication-cache cell with workload-specific
dataset identity. A cache miss receives one fresh retry, never an in-place
resume. Only a complete positive lifecycle or a classified held-out
capacity/rejection result can be promoted. The canonical diagnostic artifact
retains every cell’s receipt/cache reference, query-partition digest, selected
search effort, request-error evidence, memory timeline, final allocated volume,
and insert_duration + optimize_duration build time.
Output and retention
The canonical artifact contains report.md, normalized result.json,
provenance, a receipt, selected settings, the raw capped-concurrency JSON/CSV
diagnostic, memory rows and timelines, logs, supervisor/status evidence, and
upstream result JSON. Standard publication runs VectorDBBench’s increasing
concurrency progression at 1, 5, 10, 20, and 30 clients. Normalized rows report
peak QPS and its concurrency as well as qps_c30 and p99_c30_ms.
A lifecycle that completes the selected-setting, held-out capped sweep but has
explicit nonzero request rejections is retained as a classified capacity
terminal negative. It is reusable only with the exact concurrency schedule,
query partition, selected setting, full memory timeline, quiet-host assertion,
logs, and supervisor evidence. Setup/index/configuration failures, crashes,
timeouts, partial lifecycles, and unknown failures are never reusable
publication cells.
Database volumes are deleted only after the verified final capture unless an
operator explicitly opts into retaining them for debugging. The retained
storage.json receipt and complete optional-trial evidence are results; the
live volume is not.
Evidence status availability · expected contract
Why this lane has no selected measurement
No reviewed benchmark result is selected for this subject.
- Expected
- Reviewed aggregate with a validation receipt
- Observed
- No result selected for this subject