90 days, 90M trades
Break-even vs Polars: Deptho Pro is ahead from the first replay, ingest included.
Run 20261028T200001Z. Generated from results/latest.json by report.py; do not edit by hand.
Workload: 90 daily synthetic Binance-format aggTrades CSVs, 90,000,000 trades (5.8 GB CSV). Queries: 1s OHLCV bars over all rows; replay of 10,000 market orders (fill model trade_prints: VWAP of following same-side trade prints; no book depth, no queue position).
Machine: Intel(R) Xeon(R) Processor @ 2.10GHz, 4 logical CPUs, 15.7 GB RAM, Ubuntu 24.04.5 LTS (kernel 6.18.44-fc-v180). Python 3.13.17, numpy 2.5.3, pandas 3.0.5, pyarrow 26.0.0, Polars 2.1.0, DuckDB 2.0.0, deptho-pro 0.1.0 (commit 10a077e, rustc 1.97.0 (2d8144b78 2026-07-07)).
Each step runs in a fresh process. Cold = after sync; echo 3 > /proc/sys/vm/drop_caches. pandas is single-threaded; Polars, DuckDB and Pro use all cores.
Note: DuckDB replay carried forward from run 20261027T230334Z (same machine type, versions, data and DuckDB code; the Pro change doesn't touch it). Re-measuring it here failed: DuckDB's temporary files for this replay needed more than the 9 GB of free disk this container had.
Replay: 10,000 orders across the full period
| warm (median of 3) | cold cache | peak heap | peak RSS | lines of code | |
|---|---|---|---|---|---|
| pandas + Parquet | 13.0 s | 16.8 s | 7.4 GB | 7.5 GB | 17 |
| Polars + Parquet | 13.2 s | 23.1 s | 9.1 GB | 9.2 GB | 20 |
| DuckDB (native table) | 118.7 s | 116.8 s | 11.6 GB | 11.7 GB | 21 |
| Deptho Pro | 0.10 s | 1.13 s | 70 MB | 1.4 GB | 3 |
1-second bars
| warm (median of 3) | cold cache | peak heap | peak RSS | lines of code | |
|---|---|---|---|---|---|
| pandas + Parquet | 11.6 s | 18.4 s | 6.8 GB | 6.9 GB | 5 |
| Polars + Parquet | 2.69 s | 3.49 s | 2.4 GB | 2.5 GB | 6 |
| DuckDB (native table) | 3.27 s | 2.72 s | 2.3 GB | 2.4 GB | 5 |
| Deptho Pro | 1.91 s | 2.11 s | 1017 MB | 3.1 GB | 1 |
Ingest
| first 89 days | add 1 day | on disk | lines of code | |
|---|---|---|---|---|
| pandas + Parquet | 74.4 s | 1.06 s | 1.0 GB | 4 |
| Polars + Parquet | 15.4 s | 0.25 s | 545 MB | 3 |
| DuckDB (native table) | 33.3 s | 0.36 s | 536 MB | 5 |
| Deptho Pro | 11.2 s | 0.18 s | 2.1 GB | 4 |
Where Polars wins
From this run; a line appears only where Polars measured ahead of Pro.
- Storage: 545 MB on disk vs 2.1 GB for Pro (3.9x smaller). Parquet is compressed; Pro's store is raw columns, so it can be memory-mapped.
pandas parse peak (one daily file)
pd.read_csv of one 1,000,000-row daily file: finished frame 47 MB; heap grew by up to 86 MB while parsing (1.8x the frame; measured above the 64 MB already in use after imports), 0.51 s. Median of 3 fresh processes.
Correctness
bars pass = the same bars as pandas, values within 1/100 of a price tick. replay pass = the same unfilled orders as pandas, and 200 sampled fills within 1e-7 relative of an exact math.fsum recomputation (about 6% of a tick here). The difference from pandas is shown but does not gate replay. The pandas, Polars and DuckDB implementations compute VWAP by subtracting large running sums, so their own error grows with data size; the engine kernel sums directly.
bars
| result | max abs diff vs pandas | |
|---|---|---|
| pandas + Parquet | pass | 0.0e+00 |
| Polars + Parquet | pass | 2.8e-14 |
| DuckDB (native table) | pass | 2.8e-14 |
| Deptho Pro | pass | 2.8e-14 |
replay
| result | max abs diff vs pandas | max rel error vs exact | |
|---|---|---|---|
| pandas + Parquet | pass | 0.0e+00 | 1.1e-08 |
| Polars + Parquet | pass | 0.0e+00 | 1.1e-08 |
| Deptho Pro | pass | 1.0e-03 | 1.6e-15 |
| DuckDB (native table) | pass | 1.7e-03 | 1.2e-08 |
How to read this
- Warm means the store's files are already in the OS page cache, e.g. you just ran another query. Cold means right after the cache was dropped, like the first query after a reboot or after other work pushed the files out of memory.
- Peak heap is anonymous (non-file-backed) memory: what actually competes with your notebook for RAM. Peak RSS also counts memory-mapped file pages, which the OS can evict whenever it needs to. For Pro, most of its RSS is that file cache.
- Lines of code counts the benchmark implementation in
impls/(non-blank, non-comment). For Pro that is the user's code; the work happens in the compiled engine (packages/deptho-pro/src/lib.rs).
Memory cap: method and peaks
Run 20261028T200124Z. Generated from results/memcap-90d.json by report.py; do not edit by hand.
Workload: replay of 10,000 market orders across 90 days / 90,000,000 synthetic Binance-format trades, each engine on the store built by the scaling run.
How: cgroup v1 memory controller: memory.limit_in_bytes = memory.memsw.limit_in_bytes = cap, swappiness 0, no swap on host. Page cache is charged to the cgroup. First run after dropping the page cache (cold), then warm runs. Over the cap = OOM-killed. Timeout 900 s per run.
Note: DuckDB at 2 GB carried forward from run 20261028T140131Z: re-measuring it here failed when DuckDB's temporary files filled the container's disk. Every other cell is from this run.
Machine: Intel(R) Xeon(R) Processor @ 2.10GHz, 4 logical CPUs, 15.7 GB RAM, Ubuntu 24.04.5 LTS.
| 2.0 GB cap | 1.0 GB cap | 512 MB cap | |
|---|---|---|---|
| pandas + Parquet | out of memory (killed) | out of memory (killed) | out of memory (killed) |
| Polars + Parquet | out of memory (killed) | out of memory (killed) | out of memory (killed) |
| DuckDB (native table) | out of memory (killed) | out of memory (killed) | out of memory (killed) |
| Deptho Pro | completed: 0.11 s warm, 1.05 s cold | completed: 1.33 s warm, 1.24 s cold | completed: 1.38 s warm, 1.31 s cold |
Peak memory of completed runs (heap = process's own allocations; RSS adds mapped file pages; cgroup peak = everything charged to the cap, including page cache):
| cap | peak heap | peak RSS | cgroup peak | |
|---|---|---|---|---|
| Deptho Pro | 2.0 GB | 69 MB | 1.4 GB | 1.9 GB |
| Deptho Pro | 1.0 GB | 69 MB | 968 MB | 1.0 GB |
| Deptho Pro | 512 MB | 69 MB | 510 MB | 512 MB |
30 days, 30M trades
Break-even vs Polars: Deptho Pro takes 2.13 s longer to ingest all 30 days, then saves 2.91 s per warm replay, so it's ahead from replay 1 on. Here each engine reloads its data for every replay, as in the replay benchmark.
Run 20261028T194458Z. Generated from results/latest.json by report.py; do not edit by hand.
Workload: 30 daily synthetic Binance-format aggTrades CSVs, 30,000,000 trades (1.9 GB CSV). Queries: 1s OHLCV bars over all rows; replay of 10,000 market orders (fill model trade_prints: VWAP of following same-side trade prints; no book depth, no queue position).
Machine: Intel(R) Xeon(R) Processor @ 2.10GHz, 4 logical CPUs, 15.7 GB RAM, Ubuntu 24.04.5 LTS (kernel 6.18.44-fc-v180). Python 3.13.17, numpy 2.5.3, pandas 3.0.5, pyarrow 26.0.0, Polars 2.1.0, DuckDB 2.0.0, deptho-pro 0.1.0 (commit 10a077e, rustc 1.97.0 (2d8144b78 2026-07-07)).
Each step runs in a fresh process. Cold = after sync; echo 3 > /proc/sys/vm/drop_caches. pandas is single-threaded; Polars, DuckDB and Pro use all cores.
Replay: 10,000 orders across the full period
| warm (median of 3) | cold cache | peak heap | peak RSS | lines of code | |
|---|---|---|---|---|---|
| pandas + Parquet | 4.64 s | 6.24 s | 2.6 GB | 2.6 GB | 17 |
| Polars + Parquet | 2.97 s | 2.87 s | 3.6 GB | 3.7 GB | 20 |
| DuckDB (native table) | 16.1 s | 21.8 s | 9.0 GB | 9.1 GB | 21 |
| Deptho Pro | 0.06 s | 0.58 s | 69 MB | 780 MB | 3 |
1-second bars
| warm (median of 3) | cold cache | peak heap | peak RSS | lines of code | |
|---|---|---|---|---|---|
| pandas + Parquet | 3.87 s | 7.31 s | 2.4 GB | 2.4 GB | 5 |
| Polars + Parquet | 0.86 s | 0.96 s | 995 MB | 1.1 GB | 6 |
| DuckDB (native table) | 0.93 s | 1.16 s | 862 MB | 957 MB | 5 |
| Deptho Pro | 0.66 s | 0.74 s | 384 MB | 1.1 GB | 1 |
Ingest
| first 29 days | add 1 day | on disk | lines of code | |
|---|---|---|---|---|
| pandas + Parquet | 18.6 s | 0.66 s | 347 MB | 4 |
| Polars + Parquet | 3.86 s | 0.14 s | 181 MB | 3 |
| DuckDB (native table) | 9.89 s | 0.48 s | 176 MB | 5 |
| Deptho Pro | 5.92 s | 0.21 s | 715 MB | 4 |
Where Polars wins
From this run; a line appears only where Polars measured ahead of Pro.
- Ingest the first 29 days: 3.86 s vs 5.92 s for Pro (1.5x faster).
- Add one day: 0.14 s vs 0.21 s for Pro (1.5x faster).
- Storage: 181 MB on disk vs 715 MB for Pro (3.9x smaller). Parquet is compressed; Pro's store is raw columns, so it can be memory-mapped.
pandas parse peak (one daily file)
pd.read_csv of one 1,000,000-row daily file: finished frame 47 MB; heap grew by up to 87 MB while parsing (1.9x the frame; measured above the 64 MB already in use after imports), 0.59 s. Median of 3 fresh processes.
Correctness
bars pass = the same bars as pandas, values within 1/100 of a price tick. replay pass = the same unfilled orders as pandas, and 200 sampled fills within 1e-7 relative of an exact math.fsum recomputation (about 6% of a tick here). The difference from pandas is shown but does not gate replay. The pandas, Polars and DuckDB implementations compute VWAP by subtracting large running sums, so their own error grows with data size; the engine kernel sums directly.
bars
| result | max abs diff vs pandas | |
|---|---|---|
| pandas + Parquet | pass | 0.0e+00 |
| Polars + Parquet | pass | 1.4e-14 |
| DuckDB (native table) | pass | 1.4e-14 |
| Deptho Pro | pass | 1.4e-14 |
replay
| result | max abs diff vs pandas | max rel error vs exact | |
|---|---|---|---|
| pandas + Parquet | pass | 0.0e+00 | 1.4e-09 |
| Polars + Parquet | pass | 5.8e-11 | 1.4e-09 |
| DuckDB (native table) | pass | 3.6e-04 | 4.5e-09 |
| Deptho Pro | pass | 2.9e-04 | 1.8e-15 |
How to read this
- Warm means the store's files are already in the OS page cache, e.g. you just ran another query. Cold means right after the cache was dropped, like the first query after a reboot or after other work pushed the files out of memory.
- Peak heap is anonymous (non-file-backed) memory: what actually competes with your notebook for RAM. Peak RSS also counts memory-mapped file pages, which the OS can evict whenever it needs to. For Pro, most of its RSS is that file cache.
- Lines of code counts the benchmark implementation in
impls/(non-blank, non-comment). For Pro that is the user's code; the work happens in the compiled engine (packages/deptho-pro/src/lib.rs).
Laptop, real data: 30 days, 36M trades
Break-even vs Polars: Deptho Pro takes 5.08 s longer to ingest all 30 days, then saves 0.91 s per warm replay, so it's ahead from replay 6 on. Here each engine reloads its data for every replay, as in the replay benchmark.
Run 20261029T115838Z. Generated from results/latest.json by report.py; do not edit by hand.
Workload: 30 daily Binance-format aggTrades CSVs, 36,036,629 trades (2.2 GB CSV). Queries: 1s OHLCV bars over all rows; replay of 10,000 market orders (fill model trade_prints: VWAP of following same-side trade prints; no book depth, no queue position).
Machine: Apple M4, 10 logical CPUs, 16.0 GB RAM, macOS 26.3.1 (kernel 25.3.0). Python 3.14.0, numpy 2.5.4, pandas 3.1.0, pyarrow 26.0.0, Polars 2.1.0, DuckDB 2.0.0, deptho-pro 0.1.0 (commit 6c55071, rustc 1.99.1 (58cf8f9c3 2026-10-13)).
Each step runs in a fresh process. Cold = after sudo purge. pandas is single-threaded; Polars, DuckDB and Pro use all cores.
Replay: 10,000 orders across the full period
| warm (median of 3) | cold cache | peak heap | peak RSS | lines of code | |
|---|---|---|---|---|---|
| pandas + Parquet | 0.99 s | 1.24 s | 3.0 GB | 2.9 GB | 17 |
| Polars + Parquet | 0.92 s | 1.43 s | 3.2 GB | 3.2 GB | 20 |
| DuckDB (native table) | 6.56 s | 7.84 s | 9.0 GB | 5.6 GB | 21 |
| Deptho Pro | 0.02 s | 0.58 s | 76 MB | 493 MB | 3 |
1-second bars
| warm (median of 3) | cold cache | peak heap | peak RSS | lines of code | |
|---|---|---|---|---|---|
| pandas + Parquet | 0.76 s | 1.05 s | 2.7 GB | 2.8 GB | 5 |
| Polars + Parquet | 0.29 s | 0.44 s | 992 MB | 1.0 GB | 6 |
| DuckDB (native table) | 0.23 s | 0.41 s | 1.1 GB | 1.1 GB | 5 |
| Deptho Pro | 0.23 s | 1.69 s | 516 MB | 1.4 GB | 1 |
Ingest
| first 29 days | add 1 day | on disk | lines of code | |
|---|---|---|---|---|
| pandas + Parquet | 4.63 s | 0.20 s | 345 MB | 4 |
| Polars + Parquet | 2.06 s | 0.07 s | 196 MB | 3 |
| DuckDB (native table) | 4.48 s | 0.21 s | 218 MB | 5 |
| Deptho Pro | 6.40 s | 0.81 s | 859 MB | 4 |
Where Polars wins
From this run; a line appears only where Polars measured ahead of Pro.
- Ingest the first 29 days: 2.06 s vs 6.40 s for Pro (3.1x faster).
- Add one day: 0.07 s vs 0.81 s for Pro (11.5x faster).
- Storage: 196 MB on disk vs 859 MB for Pro (4.4x smaller). Parquet is compressed; Pro's store is raw columns, so it can be memory-mapped.
pandas parse peak (one daily file)
pd.read_csv of one 1,324,889-row daily file: finished frame 62 MB; heap grew by up to 181 MB while parsing (2.9x the frame; measured above the 69 MB already in use after imports), 0.03 s. Median of 3 fresh processes.
Correctness
bars pass = the same bars as pandas, values within 1/100 of a price tick. replay pass = the same unfilled orders as pandas, and 200 sampled fills within 1e-7 relative of an exact math.fsum recomputation (about 6% of a tick here). The difference from pandas is shown but does not gate replay. The pandas, Polars and DuckDB implementations compute VWAP by subtracting large running sums, so their own error grows with data size; the engine kernel sums directly.
bars
| result | max abs diff vs pandas | |
|---|---|---|
| pandas + Parquet | pass | 0.0e+00 |
| Polars + Parquet | pass | 5.5e-11 |
| DuckDB (native table) | pass | 5.5e-11 |
| Deptho Pro | pass | 5.5e-11 |
replay
| result | max abs diff vs pandas | max rel error vs exact | |
|---|---|---|---|
| pandas + Parquet | pass | 0.0e+00 | 1.6e-08 |
| Polars + Parquet | pass | 0.0e+00 | 1.6e-08 |
| DuckDB (native table) | pass | 2.6e-03 | 1.6e-08 |
| Deptho Pro | pass | 1.7e-03 | 2.9e-15 |
How to read this
- Warm means the store's files are already in the OS page cache, e.g. you just ran another query. Cold means right after the cache was dropped, like the first query after a reboot or after other work pushed the files out of memory.
- Peak heap is anonymous (non-file-backed) memory: what actually competes with your notebook for RAM. Peak RSS also counts memory-mapped file pages, which the OS can evict whenever it needs to. For Pro, most of its RSS is that file cache.
- Lines of code counts the benchmark implementation in
impls/(non-blank, non-comment). For Pro that is the user's code; the work happens in the compiled engine (packages/deptho-pro/src/lib.rs).