Chart · milliseconds
filter into a new table.
neo 0.380 ms in the library run. Closest: Polars at 2.34×. Farthest: DuckDB at 106×.
Embed anywhere.
<iframe src="https://n0.is/embed/chart/filter-table" title="filter into a new table" width="100%" height="520" loading="lazy" sandbox="allow-scripts" referrerpolicy="no-referrer" style="border:0;border-radius:12px"></iframe>Every engine's multiple of neo
- neo1×
- Polars2.34× · 0.889 vs 0.380
- pandas31.1× · 11.8 vs 0.380
- Spark57.8× · 22.0 vs 0.380
- DuckDB106× · 40.3 vs 0.380
Bars to scale, from zero to the slowest engine. The gray pair is the engine's measurement and neo's in the same run, in ms.
What this measures
Keep the rows whose amount is over 500 and build a new table of them, about half the file, every column copied.
What neo is held to
The same rows every other engine keeps, every column copied, checked by the same line.
Each side, this kernel only
Polars2.34× neo · neo vs Polars
timed("filter_mat", lambda: df.filter(pred))pandas31.1× neo · neo vs pandas
timed("filter_mat", lambda: df[amount > 500])Spark57.8× neo · neo vs Spark
timed("filter_mat", lambda: sink(df.filter(pred)))DuckDB106× neo · neo vs DuckDB
timed("filter_mat", lambda: con.execute("CREATE OR REPLACE TABLE f AS SELECT * FROM t WHERE amount > 500"))neo · confidential
neo's kernels, the ports written in-house to beat them, and the techniques behind both stay with the engine. Nothing about how the numbers were made is withheld: the method, the checks every engine had to pass, every number and every place neo is behind are on this page. The engine itself we keep confidential, because we don't want it getting into the wrong hands. The binaries run on request and reproduce every number here.
Measured on Apple M5 Max, 18 cores, 26.4.1, 2026-10-11. Every number here is also on the comparison page of its engine, with how it was taken and the caveats.