Benchmarks

pandas, Polars, DuckDB and Deptho Pro. Within each run, every engine gets the same Binance-format tick data on the same machine. Every number below is generated from the results files in the repo.

90-day replay under a memory cap

warm / cold · swap off · Method

Engine2 GB cap1 GB cap512 MB cap
pandas + ParquetOut of memoryOut of memoryOut of memory
Polars + ParquetOut of memoryOut of memoryOut of memory
DuckDBOut of memoryOut of memoryOut of memory
Deptho Pro0.11 s / 1.05 s1.33 s / 1.24 s1.38 s / 1.31 s
  • pandas + Parquet

    512 MB cap
    Out of memory
    1 GB cap
    Out of memory
    2 GB cap
    Out of memory
  • Polars + Parquet

    512 MB cap
    Out of memory
    1 GB cap
    Out of memory
    2 GB cap
    Out of memory
  • DuckDB

    512 MB cap
    Out of memory
    1 GB cap
    Out of memory
    2 GB cap
    Out of memory
  • Deptho Pro

    512 MB cap
    1.38 s / 1.31 s
    1 GB cap
    1.33 s / 1.24 s
    2 GB cap
    0.11 s / 1.05 s

Laptop, real data

MacBook Air (Apple M4, 16 GB) · 30 days of BTCUSDT from Binance's archive · Full results

EngineWarmColdPeak heapPeak RSSLines
pandas + Parquet0.99 s1.24 s3 GB2.9 GB17
Polars + Parquet0.92 s1.43 s3.2 GB3.2 GB20
DuckDB6.56 s7.84 s9 GB5.6 GB21
Deptho Pro0.02 s0.58 s76 MB493 MB3

Replay of 10,000 market orders across 30 days / 36,036,629 Binance-format trades (trade-print fill model), each engine on its own idiomatic storage; every engine produces the same fills. Warm: files in page cache. Cold: page cache dropped. Peak heap is memory the process allocates for itself; peak RSS also counts memory-mapped file pages, which for Pro is mostly the dataset cached by the OS (evictable whenever it needs the space). Apple M4, 10 CPUs, 16 GB RAM, macOS 26.3.1.

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 1,000 backtest replays over 90 days of tick data in 17 seconds and 80 MB of memory, ingest included.

total time · peak heap · Method

Same replays, same machine: pandas: 24 s, 7.5 GB; Polars: 87 s, 11.1 GB (total time, peak heap).

EngineTotal timePeak heapIngestLoad + precomputePer replayHeld heap
pandas + Parquet24 s7.5 GB12 s6.40 s3.11 ms7.5 GB
Polars + Parquet87 s11.1 GB6.04 s6.82 s70.7 ms8.1 GB
Deptho Pro17 s80 MB11 s0.2 ms3.82 ms76 MB

1,000 replays of 10,000 market orders each (seeds 1..1000) over 90 days / 90,000,000 trades. Each engine ingests, loads once, precomputes anything that doesn't depend on the orders, then replays; total = ingest + load + replays. Held heap: memory still allocated after loading, for the whole sweep. Budget 3600 s of load + replay time per engine. Trades are synthetic, generated in Binance's archive format. Apple M4, 10 CPUs, 16 GB RAM.

Where pandas wins

  • Each replay, once loaded: 3.11 ms vs 3.82 ms for Deptho Pro (1.2x faster). pandas first spends 19 s ingesting and precomputing running sums, then holds 7.5 GB for the whole sweep. At these rates it would overtake Deptho Pro's total after about 11,193 replays.

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 cachepeak heappeak RSSlines of code
pandas + Parquet13.0 s16.8 s7.4 GB7.5 GB17
Polars + Parquet13.2 s23.1 s9.1 GB9.2 GB20
DuckDB (native table)118.7 s116.8 s11.6 GB11.7 GB21
Deptho Pro0.10 s1.13 s70 MB1.4 GB3

1-second bars

warm (median of 3)cold cachepeak heappeak RSSlines of code
pandas + Parquet11.6 s18.4 s6.8 GB6.9 GB5
Polars + Parquet2.69 s3.49 s2.4 GB2.5 GB6
DuckDB (native table)3.27 s2.72 s2.3 GB2.4 GB5
Deptho Pro1.91 s2.11 s1017 MB3.1 GB1

Ingest

first 89 daysadd 1 dayon disklines of code
pandas + Parquet74.4 s1.06 s1.0 GB4
Polars + Parquet15.4 s0.25 s545 MB3
DuckDB (native table)33.3 s0.36 s536 MB5
Deptho Pro11.2 s0.18 s2.1 GB4

Where Polars wins

From this run; a line appears only where Polars measured ahead of Pro.

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

resultmax abs diff vs pandas
pandas + Parquetpass0.0e+00
Polars + Parquetpass2.8e-14
DuckDB (native table)pass2.8e-14
Deptho Propass2.8e-14

replay

resultmax abs diff vs pandasmax rel error vs exact
pandas + Parquetpass0.0e+001.1e-08
Polars + Parquetpass0.0e+001.1e-08
Deptho Propass1.0e-031.6e-15
DuckDB (native table)pass1.7e-031.2e-08

How to read this

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 cap1.0 GB cap512 MB cap
pandas + Parquetout of memory (killed)out of memory (killed)out of memory (killed)
Polars + Parquetout 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 Procompleted: 0.11 s warm, 1.05 s coldcompleted: 1.33 s warm, 1.24 s coldcompleted: 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):

cappeak heappeak RSScgroup peak
Deptho Pro2.0 GB69 MB1.4 GB1.9 GB
Deptho Pro1.0 GB69 MB968 MB1.0 GB
Deptho Pro512 MB69 MB510 MB512 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 cachepeak heappeak RSSlines of code
pandas + Parquet4.64 s6.24 s2.6 GB2.6 GB17
Polars + Parquet2.97 s2.87 s3.6 GB3.7 GB20
DuckDB (native table)16.1 s21.8 s9.0 GB9.1 GB21
Deptho Pro0.06 s0.58 s69 MB780 MB3

1-second bars

warm (median of 3)cold cachepeak heappeak RSSlines of code
pandas + Parquet3.87 s7.31 s2.4 GB2.4 GB5
Polars + Parquet0.86 s0.96 s995 MB1.1 GB6
DuckDB (native table)0.93 s1.16 s862 MB957 MB5
Deptho Pro0.66 s0.74 s384 MB1.1 GB1

Ingest

first 29 daysadd 1 dayon disklines of code
pandas + Parquet18.6 s0.66 s347 MB4
Polars + Parquet3.86 s0.14 s181 MB3
DuckDB (native table)9.89 s0.48 s176 MB5
Deptho Pro5.92 s0.21 s715 MB4

Where Polars wins

From this run; a line appears only where Polars measured ahead of Pro.

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

resultmax abs diff vs pandas
pandas + Parquetpass0.0e+00
Polars + Parquetpass1.4e-14
DuckDB (native table)pass1.4e-14
Deptho Propass1.4e-14

replay

resultmax abs diff vs pandasmax rel error vs exact
pandas + Parquetpass0.0e+001.4e-09
Polars + Parquetpass5.8e-111.4e-09
DuckDB (native table)pass3.6e-044.5e-09
Deptho Propass2.9e-041.8e-15

How to read this

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 cachepeak heappeak RSSlines of code
pandas + Parquet0.99 s1.24 s3.0 GB2.9 GB17
Polars + Parquet0.92 s1.43 s3.2 GB3.2 GB20
DuckDB (native table)6.56 s7.84 s9.0 GB5.6 GB21
Deptho Pro0.02 s0.58 s76 MB493 MB3

1-second bars

warm (median of 3)cold cachepeak heappeak RSSlines of code
pandas + Parquet0.76 s1.05 s2.7 GB2.8 GB5
Polars + Parquet0.29 s0.44 s992 MB1.0 GB6
DuckDB (native table)0.23 s0.41 s1.1 GB1.1 GB5
Deptho Pro0.23 s1.69 s516 MB1.4 GB1

Ingest

first 29 daysadd 1 dayon disklines of code
pandas + Parquet4.63 s0.20 s345 MB4
Polars + Parquet2.06 s0.07 s196 MB3
DuckDB (native table)4.48 s0.21 s218 MB5
Deptho Pro6.40 s0.81 s859 MB4

Where Polars wins

From this run; a line appears only where Polars measured ahead of Pro.

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

resultmax abs diff vs pandas
pandas + Parquetpass0.0e+00
Polars + Parquetpass5.5e-11
DuckDB (native table)pass5.5e-11
Deptho Propass5.5e-11

replay

resultmax abs diff vs pandasmax rel error vs exact
pandas + Parquetpass0.0e+001.6e-08
Polars + Parquetpass0.0e+001.6e-08
DuckDB (native table)pass2.6e-031.6e-08
Deptho Propass1.7e-032.9e-15

How to read this