— Benchmarks
Measured,
not claimed
- Machine
- Apple M1 Pro, 8 cores, 16 GB, Node 25
- Against
- DuckDB 2.0 (@duckdb/node-api 2.0.0-r.1)
- Vectro
- b2a3033 · 2026-11-01
- Load
- Shared laptop: load 3–8 during the in-memory runs, up to 15 during the billion-row run. Ratios move by up to about ±30% between runs.
- Method
- Each system in its own process, best of 5 in memory and best of 3 on disk, every answer compared between the two.
In memory · 10M rows
The same SQL on 10M orders + 100K customers
The same SQL text in both, from filters and joins to window functions, percentiles, text functions and outer joins.
20/20
faster or level · best of 5, answers checked · compare/bench-sql.mjs
Vectro DuckDB · lower is better
In memory · 10M rows
Group-by on 10M rows
count(*) and sum(price * qty) per group, from 2,920 groups to 5.7 million.
4/4
faster or level · count(*) and sum(price * qty) per group, best of 5 · compare/bench-groupby.mjs
Vectro DuckDB · lower is better
In memory · 10M rows
Joins on 10M orders
The joined table built in full, then counted and summed.
4/4
faster or level · joined table built in full, then counted and summed, best of 5 · compare/bench-join.mjs
Vectro DuckDB · lower is better
In memory · 10M rows
Distinct counts on 10M rows
Exact distinct counts. The 1M accounts are evenly spaced IDs (1e9 + 7919k); 1M random IDs up to 2^40, with no common step, take 24 ms against DuckDB’s 46.
4/4
faster or level · exact; best of 5 · compare/bench-distinct.mjs
Vectro DuckDB · lower is better
On disk · bigger than memory
A Vectro dataset against a DuckDB database file
The same rows in both, made from whole-number formulas exact in JavaScript and in DuckDB, so the answers can be compared. Each query runs in a fresh process; the first run and the best of 3 are both recorded.
100M rows
| Query | Vectro | DuckDB | |
|---|---|---|---|
| scan everything | 70 ms | 73 ms | level |
| filter: EU, qty >= 5 | 100 ms | 87 ms | 1.1× slower |
| the last day (time range) | 3 ms | 1 ms | 3.0× slower |
| group by region | 142 ms | 149 ms | level |
| top 10 of 1M customers | 529 ms | 637 ms | 1.2× faster |
| top 10 rows by price | 15 ms | 100 ms | 6.7× faster |
| join 1M customers | 126 ms | 85 ms | 1.5× slower |
| distinct customers per region | 268 ms | 650 ms | 2.4× faster |
| file on disk | 0.5 GB | 0.5 GB | |
| peak memory (RSS) | 344 MB | 2.9 GB |
1 billion rows
| Query | Vectro | DuckDB | |
|---|---|---|---|
| scan everything | 478 ms | 662 ms | 1.4× faster |
| filter: EU, qty >= 5 | 691 ms | 729 ms | 1.1× faster |
| the last day (time range) | 3 ms | 4 ms | 1.3× faster |
| group by region | 1.05 s | 1.25 s | 1.2× faster |
| top 10 of 1M customers | 4.39 s | 4.50 s | level |
| top 10 rows by price | 11 ms | 843 ms | 76.6× faster |
| join 1M customers | 880 ms | 627 ms | 1.4× slower |
| distinct customers per region | 2.43 s | 6.84 s | 2.8× faster |
| file on disk | 5.1 GB | 5.4 GB | |
| peak memory (RSS) | 0.37 GB | 6.9 GB |
Where DuckDB is still faster
Every in-memory query above is faster or level. Join 1M customers: 1.5× slower at 100M rows, 1.4× at a billion; filter: EU, qty >= 5: 1.1× slower at 100M; the last day: 3 vs 1 ms at 100M. They are listed as they are.
Run them yourself
cd compare && npm install node bench-sql.mjs node bench-groupby.mjs node bench-join.mjs node bench-distinct.mjs ROWS=1000000000 node bench-dataset.mjs
Or race Vectro, DuckDB-WASM and Arquero in this browser on the live page.
