Chart · milliseconds

filter amount > 500, count.

neo 0.018 ms in the language study, 0.018 ms in the library run. Closest: Zig at 1.67×. Farthest: Spark at 2,049×.

—

neo crosses once

neo

TAKE THIS CHART WITH YOU

Embed anywhere.

<iframe src="https://n0.is/embed/chart/filter" title="filter amount &gt; 500, count" 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

  1. neo
    1×
  2. Zig
    1.67× · 0.030 vs 0.018
  3. Rust
    2.06× · 0.037 vs 0.018
  4. Swift
    2.33× · 0.042 vs 0.018
  5. Go
    3.56× · 0.064 vs 0.018
  6. Polars
    9.94× · 0.179 vs 0.018
  7. pandas
    18.2× · 0.328 vs 0.018
  8. DuckDB
    33.5× · 0.602 vs 0.018
  9. Spark
    2,049× · 36.9 vs 0.018

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. The languages and the libraries were measured in separate runs, so neo has a number in each; every multiple is against neo in its own run.

What this measures

Count the rows whose amount is over 500: a million comparisons on a typed column, the answer one integer. Each library's count is written the way its users write it.

What neo is held to

The count every other engine prints, under the JavaScript node's rule that NaN never matches. What comes out is a selection the next kernel reads from, not a copy of the rows.

Each side, this kernel only

Zig1.67× neo · neo vs Zig

Written in-house; it stays with the engine. How it was run and checked is on its comparison page.

Rust2.06× neo · neo vs Rust

Written in-house; it stays with the engine. How it was run and checked is on its comparison page.

Swift2.33× neo · neo vs Swift

Written in-house; it stays with the engine. How it was run and checked is on its comparison page.

Go3.56× neo · neo vs Go

Written in-house; it stays with the engine. How it was run and checked is on its comparison page.

Polars9.94× neo · neo vs Polars

Polars, this kernel · neon-cpp/bench/lang/python/yardstick.py:218
timed("filter", lambda: df.lazy().filter(pred).select(pl.len()).collect().item())

pandas18.2× neo · neo vs pandas

pandas, this kernel · neon-cpp/bench/lang/python/yardstick.py:197
timed("filter", lambda: int((amount > 500).sum()))

DuckDB33.5× neo · neo vs DuckDB

DuckDB, this kernel · neon-cpp/bench/lang/python/yardstick.py:237
timed("filter", lambda: con.execute("SELECT count(*) FROM t WHERE amount > 500").fetchone())

Spark2,049× neo · neo vs Spark

Spark, this kernel · neon-cpp/bench/lang/python/yardstick.py:255
timed("filter", lambda: df.filter(pred).count())

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 and 2026-10-15. Every number here is also on the comparison page of its engine, with how it was taken and the caveats.

All evalsEvery comparisonStart a conversation